Skip to content
CSRF Defense via state
step 1/5

Reading — step 1 of 5

Read

~1 min readProduction

CSRF Defense via state

Without state, attackers can hijack OAuth flows:

1. Attacker initiates OAuth flow with their account; gets authorization URL.
2. Attacker tricks victim into clicking the URL.
3. Victim authenticates and authorizes.
4. Token gets bound to the ATTACKER's app session (because the redirect's state doesn't tie to victim).
5. Now victim's data flows to attacker's app account.

Defense: state is a cryptographically random nonce that the auth server echoes back.

1. Backend generates random state, stores in user's session: state = secrets.token_urlsafe(32)
2. Authorization URL: ...&state=Wj9XfTvB1y...
3. After redirect: GET /callback?code=XYZ&state=Wj9XfTvB1y...
4. Backend checks: received_state == session_state?
5. If NO → reject the callback as forged.

With PKCE, state is partially redundant but STILL recommended (defense in depth — state defends CSRF; PKCE defends interception).

Common bug: storing state globally instead of per-session. Then any user can complete any flow → still vulnerable.

Modern frameworks (NextAuth, oauth4webapi, authlib) handle state automatically. If you're rolling your own — DON'T skip state.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…