100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

State and CSRF Protection

How the state parameter defeats login CSRF in OAuth flows, the entropy and storage requirements that make it effective, and why it complements PKCE.

SecurityIntermediate8 min readJul 10, 2026
Analogies

The Role of the State Parameter

The state parameter is an opaque value the client generates before redirecting the user to the authorization endpoint, and the authorization server is required to return it unchanged when redirecting back to the client's callback. The client then checks that the returned state matches the value it originally issued for that specific browser session. This round trip proves the callback being processed corresponds to an authorization flow the current user's browser actually initiated, rather than one an attacker started and is now trying to complete inside the victim's session.

🏏

Cricket analogy: It's like a coin toss witnessed and recorded by both captains before the match, so neither side can later dispute which team chose to bat; state lets the client later verify this exact callback matches the flow it started.

Cryptographic Requirements for a Safe State Value

For state to actually stop CSRF, it must be unpredictable and bound to the specific user's session, not just any random-looking string. A common mistake is generating state with a weak or predictable random number generator, or storing it in a way that's shared across users, such as a global variable on the server rather than per-session storage. Best practice is to generate at least 128 bits of entropy using a cryptographically secure random number generator, store it server-side or in a signed, httpOnly cookie tied to the session, and reject the callback outright if the returned value doesn't match exactly.

🏏

Cricket analogy: A scorer using a truly random, uncrackable code to seal match predictions before the toss, rather than a guessable sequential number, mirrors why state needs cryptographic-grade randomness rather than predictable values.

State vs. PKCE: Complementary, Not Redundant

State and PKCE solve different problems and both are needed. State prevents CSRF against the authorization endpoint by ensuring the callback belongs to a flow the current browser session actually started. PKCE prevents authorization code interception by ensuring the party redeeming the code is the same party that initiated the request, regardless of which session started it. A flow can be CSRF-safe with valid state but still vulnerable to code interception without PKCE, and vice versa, which is why modern guidance requires implementing both together rather than treating either as a full substitute for the other.

🏏

Cricket analogy: Checking both the bowler's front-foot no-ball line and the wicketkeeper's stumping timing are two separate umpiring checks that catch different infractions, just as state and PKCE catch different classes of attack.

python
import secrets
from flask import session, redirect, request, abort

@app.route("/login")
def login():
    state = secrets.token_urlsafe(32)  # >=128 bits of entropy
    session["oauth_state"] = state
    authorize_url = (
        "https://authz.example.com/authorize"
        f"?response_type=code&client_id={CLIENT_ID}"
        f"&redirect_uri={CALLBACK_URL}&scope=openid profile"
        f"&state={state}"
        f"&code_challenge={code_challenge}&code_challenge_method=S256"
    )
    return redirect(authorize_url)

@app.route("/callback")
def callback():
    returned_state = request.args.get("state")
    expected_state = session.pop("oauth_state", None)
    if not expected_state or not secrets.compare_digest(returned_state or "", expected_state):
        abort(400, "Invalid or missing state parameter")
    # proceed to exchange the authorization code for tokens

Validate state with a constant-time comparison and always pull the expected value from server-side or signed session storage, never from a client-supplied cookie the attacker could also set.

  • State ties the authorization callback back to the exact browser session that initiated the flow.
  • Without state validation, an attacker can force login CSRF by completing the flow using the victim's browser.
  • State must be generated with a cryptographically secure random number generator, at least 128 bits of entropy.
  • State should be stored server-side or in a signed, httpOnly cookie, never in a globally shared variable.
  • State and PKCE are complementary: state stops CSRF, PKCE stops code interception, and both are required.
  • Comparison of returned state to expected state must be exact and ideally constant-time.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#OAuth20StudyNotes#StateAndCSRFProtection#State#CSRF#Protection#Role#Security#StudyNotes#SkillVeris