Fwio

How does the "state" parameter prevent CSRF in OAuth?

3 min

The purpose of the state parameter had been a mystery to me for a long time. I kept running into those vague “preventing CSRF attacks” explanations, but they never clicked. (I even spent an hour talking to ChatGPT about it - only to realize it was confidently making stuff up again 😅.)

Today, while reading The Copenhagen Book, I came across this concept once more and finally decided to dig in and understand what this mysterious, echoed parameter is all about.

”Prevent CSRF” - but how, exactly?

I’ve always found some long-standing HTTP security concepts hard to grasp, mostly because it’s tricky to picture a real-world attack scenario. That’s why I’m writing this post around a concrete example.

Everyone says that the state parameter prevents CSRF attacks - but how does such an attack even happen in a typical OAuth flow?

I mean, a user visits the login endpoint, gets redirected to the OAuth provider’s login page, authorizes access, and is redirected back to the client with an authorization code. Where exactly does the attacker fit into this flow?

Let’s break it down with a visual and a scenario:

Illustrating CSRF attack in an OAuth flow

In this example, there’s only one legitimate OAuth app (our client). Both the attacker and the victim interact with it.

The malicious steps are highlighted in the diagram:

  1. The attacker logs into the OAuth app, and requests an OAuth login.
  2. They confirm the OAuth authorization using their own account. An authorization code is generated and returned to the client.
  3. ⚠️ Instead of completing the login flow, the attacker extracts the one-time code and holds onto it.
  4. ⚠️ They construct a malicious link containing this code and send it to the victim (via a crafted image, iframe of link, etc.).
  5. The victim unknowingly opens the link.
  6. The victim’s client exchanges the code for an access token.
  7. ❌ Oh no! The victim is now authenticated to the app, but as the attacker!

Now imagine this OAuth app is a payments platform. The attacker can now initiate various transactions through their OAuth account… but it’s you who ends up paying the bill.

The state parameter, finally explained

According to the OAuth 2.0 spec, the state parameter is an opaque value used by the client to maintain state between the request and callback.

In practice, this means the client generates a random value (often encrypted or signed), sends it along with the OAuth request, and expects the exact same value to be returned by the provider.

When the client receives the callback with the authorization code, it checks that the state matches the one it originally sent. If it doesn’t match, the client aborts the flow.

How state prevent CSRF in an OAuth flow

Recap

The key to understanding how state prevent CSRF lies in realizing that this kind of CSRF attack doesn’t inject malicious actions - it authenticates the victim as the attacker, kind of the reverse of traditional CSRF.

By validating the state, the client ensures the callback is part of a legitimate session it initiated, and not one triggered by a third party using a stolen code.

Further Reading