Access control and authentication
Log in Join

Lesson 2 · Reading · 20 min

The two-account method

Here is the technique that finds more access-control bugs than any clever payload: hold two accounts, do something as one, and try to do it as the other. It sounds too simple to matter. It is the backbone of professional authorization testing, and once it is a habit you will find bugs that scanners never will.

The two-account method: do a thing as Alice, then replay the exact request as Bob

Why two accounts

Authorization is about whether this user may reach that thing. You cannot test that with one account, because everything one account touches is, by definition, its own. You need a second identity to ask the real question: "this object clearly belongs to account A, can account B reach it anyway?" If B can, the server is not checking ownership, and that is the bug.

So the very first thing you do on a new target is register (or get issued) two accounts you fully control. Call them Alice and Bob. They should be as similar as possible, same role, same plan, so that any difference in what they can reach is a genuine authorization gap and not just "Bob is an admin."

The method, step by step

  1. Log in as Alice. Do a normal action: view your profile, open an order, download an invoice, read a message. Watch the request in your proxy or DevTools.
  2. Find the reference. Note the identifier that names Alice's object, /orders/1001, ?invoice=1042, user_id=87, a UUID, a filename. That reference is the thing you will try to steal.
  3. Log in as Bob (a separate browser profile, an incognito window, or a saved second set of cookies, keep the two sessions apart so you never confuse them).
  4. Replay Alice's request as Bob. Send the exact same request, same URL, same object id, but with Bob's session cookie or token. The only thing that changed is who is asking.
  5. Read the response. If Bob gets Alice's data, the server authenticated Bob and never checked that the object was his. Finding. If Bob gets a 403, an empty result, or his own data instead, the check is working.

The whole thing is one substitution: keep the request identical, change only the identity, and see whether the boundary holds.

Set up the harness once, use it forever

Getting good at this is mostly about making the swap frictionless so you actually do it on every endpoint:

  • Two browser profiles, or one normal window and one incognito, each logged into a different account. This is the simplest setup and enough to start.
  • An intercepting proxy (Burp Suite, ZAP, mitmproxy) is the professional tool. Capture Alice's request, then resend it with Bob's Cookie/Authorization header swapped in. Burp's Repeater is built for exactly this, and its "match and replace" or extensions can automate replaying a whole session as the other user.
  • Keep a note of each account's ids: Alice owns orders 1001–1005, Bob owns 2001–2005. Now you know instantly whether a response you got as Bob was his or hers.

What to test it against: everything with a reference

Once the harness is up, walk the whole application and, at every point where a request names an object or performs an action, do the swap. Profiles, orders, invoices, messages, files, comments, settings, API objects, export endpoints. The bugs are rarely on the obvious page; they are on the third API call the mobile app makes, or the "download receipt" link, or the endpoint that updates a field. The method is the same every time: Alice's reference, Bob's session, read the answer.

The subtle wins

The swap catches more than "read another user's data":

  • Write, not just read. Can Bob modify Alice's object? Send Alice's update request with Bob's session. Editing or deleting someone else's data is often higher impact than reading it.
  • Horizontal and vertical. Swapping between two equal users is horizontal (Alice ↔ Bob). Swapping a low-privilege user's session onto a high-privilege action is vertical, that is the function-level test in lesson 4.
  • Unauthenticated replay. Sometimes the strongest version is Bob with no session at all: does the endpoint answer a request with no cookie? If a stranger can fetch /orders/1001, the check is not just weak, it is absent.

Keep it honest, and keep it yours

This is exactly why you use two accounts you own. You prove the flaw by reaching your own Alice's data as your own Bob, never by pulling a real stranger's records. On a real engagement, the moment you have shown Bob can reach one object that is provably Alice's, you have your finding; you do not then trawl through other people's real data to "confirm." One clean cross-account read, both accounts yours, is the whole proof.

Where this goes next

You have the harness. The next lesson points it at the most common target: object-level access, where the reference sits right there in the request and the server trusts it as proof of ownership. This is IDOR, in full, and the Someone Else's Order idea grown up into real applications.

This lesson has 1 lab to try what you just read.