Sequential Relay IDs in a GraphQL API
You are assessing a B2B SaaS application with a GraphQL endpoint at /graphql. You have two accounts you created: [email protected] in tenant 1042 and [email protected] in tenant 1043. The proxy history shows a query operationName UserProfile with variables {"userId":1042}. The response includes email, phone, and billing address. When you replay the request with userId 1043 using alice's session cookie, the server returns HTTP 200 with bob's data. When you try 1044, it returns HTTP 403 with message "Tenant mismatch". The API rate-limits each session to 30 requests per minute and returns 429 after that. An alert fires if more than five distinct tenant IDs are queried from one session in ten minutes. The client wants proof of impact but does not want other tenants alerted. You have Burp Suite Professional with Repeater, Intruder, Comparer, and Logger.
1. How do you confirm this is a genuine IDOR rather than a misconfigured test account, and what Burp evidence do you capture?
Read the worked answer
Use the two accounts as a control pair. In Repeater, send the UserProfile query as alice with userId=1042 and userId=1043. Confirm that 1043 returns bob's email and phone, not alice's, and that the response is not a cached or shared object. Then reverse the test: as bob, query 1042 and 1043. If bob can read alice's profile while alice can read bob's, the object-level check is missing. Inspect the full request for tenant headers or cookies, such as X-Tenant-ID, and remove or change them to see whether authorization depends on the header or on userId. Capture both directions in Logger with timestamps, and save a Comparer diff of the 1042 and 1043 responses to show distinct PII. A misconfigured test account would normally return the same data for both IDs or work only in one direction; symmetric cross-tenant reads are the evidence.
2. Given the rate limit and alerting, outline a safe enumeration plan that still proves impact. Include Burp settings.
Read the worked answer
Do not run a 1,000-ID Intruder attack. The alert threshold is five distinct tenant IDs in ten minutes, so a defensible proof can be built with the two IDs you already own plus at most two neighbours. In Intruder, use a Sniper attack on the userId value with a payload list of 1042, 1043, 1044, 1045. Set a fixed interval of 3,000 ms, about 20 requests per minute and below the 30/min limit, and disable payload encoding changes that would alter the JSON. Add Grep - Match for "email" and "billingAddress"; add Grep - Extract for the first email address. Use Logger to record timestamps and status codes. Stop when you have one confirmed cross-tenant response; 1044's 403 is a useful negative control. If the client needs a count of affected tenants, request written coordination with their SOC and use a separate, pre-agreed session rather than silently raising volume.
3. What would you recommend to fix it, and how would you verify the fix with Burp?
Read the worked answer
Fix at the resolver: derive tenant_id from the authenticated session, not from the userId argument, and enforce an object-level authorization check on every user lookup. Return 404 for objects outside the tenant to avoid a tenant-existence oracle. Verification in Repeater: as alice, query 1042 and expect 200 with alice's data; query 1043 and 1044 and expect 404 or 403 with no PII. Repeat as bob. Test the reverse direction and a freshly created third account. Also test whether a forged X-Tenant-ID: 1043 changes the result; it should be ignored. Use Comparer to confirm the 403/404 bodies do not leak email or phone. Keep the original vulnerable request and response in Logger as evidence, and re-run the same Intruder payload set to show that no cross-tenant data is returned.