Back to Feed
article 2m read

Anti-forgery check stepped aside for any request that mentioned a token

The scheduling app skips its forgery check whenever a token-named field or login header shows up, without checking the token. Whether that's reachable today depends on what each endpoint does before the guard runs, and the guard can't see that.

Article DepthPractice

Flow

Decide who is calling, then decide on the check

  1. 1Work out the caller

    Check whether the token is real before anything else.

  2. 2Real token

    Skip the form secret.

  3. 3Junk token or none

    Fall back to the normal form secret check.

Letting scripts log in to my store scheduling app with a signed token and get past its anti-forgery check was the aim. That check wants a secret tied to a browser session. A script with only a token has no session, so as far as I can read the old code, its writes were all refused.

I added three lines to the check. If the request has a field with the token's name, in the URL or the body, or has any authorization header, skip the check. I added a line to the docs saying so. There was no test.

Why a hostile page can flip it

That condition doesn't ask if the token is real. It asks if something with the right name is there, and the sender decides that. The whole point of the check is that a hostile web page can make your browser send a request to your app. That page can't read your secret, but it can add any field it likes. So a check that turns off when a field shows up hands the page the off switch.

A form can set fields in the body or the URL for free. A custom header is harder to fake from another site, but the check only looks for it, never at it.

Why it might be shut today

Probably the endpoints that take a token verify its signature and expiry first, and get the user from the token, not from a cookie. I haven't confirmed it. If so, a fake form with a junk token is turned away before the shortcut, and there's no browser login for it to borrow.

But then it's held shut by the order of lines in other files. Any endpoint that trusts the cookie and calls this check gets a bypass any web page can reach.

One smaller problem: I suspect nothing reads that header to identify the caller, so the docs may promise something that doesn't exist.

Identify first, then decide on the check

Work out who the caller is first, then let the check use that answer.

Only a verified pass skips the form secret.

A fake value then just falls back to the normal check. The test I want: a cookie request with a junk token and no form secret gets refused. My three lines shipped without it.

Keep reading

Pick up a connected idea or branch into a nearby one.

5 paths forward
Related idea

New delete button hands the server a path, and the server doesn't take its word for it

A Delete button was added to the backup page in the scheduling app. It sends a file path, so the server resolves it, fences it to the selected company's folder with a separator-aware check, refuses anything but a regular file, and only then deletes. What still bugs me: name-derived folders, hard deletes, no password, no tests and a vague audit entry.

via #security · #api-design · #web-security