
A signed-in browser will send its session cookie with a state-changing request even when another site triggered the call. A hidden form or a scripted request is enough. The attacker need not see the response. The harm is the action the site takes.
The secret in the real page closes that gap. It does not identify the person by itself. It shows the call carried a value the attacker’s page could not read. Login forms and an Application Programming Interface (API) that writes data need it. The session cookie only shows that a browser still holds the login.
How a Logged-In Browser Is Tricked
A visitor who is already signed in then loads a page the attacker controls. That page requests a state-changing Uniform Resource Locator (URL) on the real site. The browser attaches the session cookie because that site set it, not because the visitor agreed. The same-origin policy hides the response and the site’s Hypertext Markup Language (HTML) from the attacker’s script. The request still goes out.
Cross-Origin Resource Sharing (CORS) does not replace the secret. Those headers only control whether script may read a cross-origin reply. A form can change state without reading that reply.
Synchronizer Token and Double-Submit Cookie
A synchronizer token is a random secret stored on the user session and copied into the page. The form puts it in a hidden field. Script that calls an API copies it into a header the browser will not attach by itself. The server compares the two copies and rejects a missing or different value. The other origin cannot read the page, so it has no match to send.
A double-submit cookie skips the server-side store. The site sets a cookie its own pages can read and requires the same value in a field or header. The server compares those copies. The attacker’s page cannot read the cookie, so it cannot fill the field. The session cookie may still be attached on its own.
The secret does not name the user. A stolen session cookie still works in a browser the attacker controls. The check only stops another origin from forging the call inside the visitor’s browser. Cross-Site Scripting (XSS) on the real origin can read the secret and defeat the check. A JSON Web Token (JWT) is not this secret. In a cookie it still rides along, and it can name a caller without proving which page built the request.
| Check | What the Server Compares | Where It Fails |
| Synchronizer token | Field or header versus the secret stored for that session | No session store, or script on the real origin can read it |
| Double-submit cookie | Cookie versus the repeated field or header | A sibling host sets the cookie, or script cannot read it |
| Origin check | Origin or Referer host versus the site host | Header absent, or a related host is trusted |
Tie the Secret to the Session
Bind the secret to the session behind the cookie. A token from the attacker’s own login must fail when the request carries the visitor’s cookie. Replace it at session start and at a privilege change, and drop it at logout. One CSRF secret per session is enough only if it changes at those points. A cached page or a log line can otherwise be replayed.
SameSite cookies and an origin check complement the token. They do not replace it. Lax mode still sends the cookie on some top-level navigations, which leaves a state-changing link exposed. Strict mode blocks more cross-site arrivals and can also block a wanted visit from another site. An origin check compares the Origin header, or the Referer host if Origin is missing, with the host that served the page. Stripped headers and a wide allow-list are the weak spots.
- Compare, then drop it. Keep the secret out of logs and out of the address so a later Referer header cannot leak it.
- Fail closed. A missing value and a wrong value are both refusals. Do not accept the session cookie alone.
- Separate it from the login artifact. A JWT in a cookie still arrives on the forged request.