GRAPHQL, SECURITY
Depending on how permissions are validated, it’s possible to find some fun authorization issues in GraphQL APIs. This blog post dicusses that idea and introduces a new tool to make that testing easier.
Let’s imagine the following GraphQL schema
In an ideal scenario (from the developer’s perspective) the permission checks would be done on the object level, which means that no matter the path you take to reach Foo
, it’s Foo
that’s responsible for checking if you’re allowed to load it and not the Root
or Bar
objects as they load Foo
. This is how GitLab does it and from what I can tell that’s also how HackerOne does it.
Some other websites however will program their authorization logic in the code that fetches the object, this means the authorization logic might have flaws in one path but not in another one. Applied to the schema above, Root -> Foo
might be checked properly but Root -> Bar -> Foo
might not check for permissions at all. It’s fairly easy to figure out all the paths in my FooBar schema, however some schemas are very complicated and tooling would help to figure out all the possible paths.
This is where my new tool with a very original name comes in: graphql-path-enum
. Given that most graphs have loops and have an infinite amount of paths, the tool doesn’t list them all, but it does a relatively exhaustive listing nonetheless.
$ graphql-path-enum -i ./test_data/h1_introspection.json -t Skill
Found 27 ways to reach the "Skill" node from the "Query" node:
- Query (assignable_teams) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (checklist_check) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (checklist_check_response) -> ChecklistCheckResponse (checklist_check) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (checklist_checks) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (clusters) -> Cluster (weaknesses) -> Weakness (critical_reports) -> TeamMemberGroupConnection (edges) -> TeamMemberGroupEdge (node) -> TeamMemberGroup (team_members) -> TeamMember (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (embedded_submission_form) -> EmbeddedSubmissionForm (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (external_program) -> ExternalProgram (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (external_programs) -> ExternalProgram (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (job_listing) -> JobListing (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (job_listings) -> JobListing (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (me) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (pentest) -> Pentest (lead_pentester) -> Pentester (user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (pentests) -> Pentest (lead_pentester) -> Pentester (user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (query) -> Query (assignable_teams) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (query) -> Query (skills) -> Skill
- Query (report) -> Report (bounties) -> Bounty (invitations) -> InvitationsClaimBounty (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (report_retest_user) -> ReportRetestUser (invitation) -> InvitationsRetest (report) -> Report (bounties) -> Bounty (invitations) -> InvitationsClaimBounty (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (reports) -> TeamMemberGroupConnection (edges) -> TeamMemberGroupEdge (node) -> TeamMemberGroup (team_members) -> TeamMember (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (skills) -> Skill
- Query (sla_statuses) -> SlaStatus (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (teams) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (triage_inbox_items) -> TriageInboxItem (report) -> Report (bounties) -> Bounty (invitations) -> InvitationsClaimBounty (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (users) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (weaknesses) -> Weakness (critical_reports) -> TeamMemberGroupConnection (edges) -> TeamMemberGroupEdge (node) -> TeamMemberGroup (team_members) -> TeamMember (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (webhook) -> Webhook (created_by) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
$ graphql-path-enum --help
graphql-path-enum 1.0
dee-see (https://gitlab.com/dee-see/graphql-path-enum)
Use this tool to list the different paths that lead to one object in a GraphQL schema.
USAGE:
graphql-path-enum [FLAGS] --introspect-query-path <FILE_PATH> --type <TYPE_NAME>
FLAGS:
--expand-connections Expand connection nodes (with pageInfo, edges, etc. edges), they are skipped by default
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-i, --introspect-query-path <FILE_PATH> Path to the introspection query result saved as JSON
-t, --type <TYPE_NAME> The type to look for in the graph.
It’s open source and can be found here. Let me know if there are issues or cool features that could be added. It’s my first time writing in Rust so it might not be perfect, code reviews accepted and appreciated!
Happy hacking!