Summary
XSS injects script into a page; CSRF forges cross-site requests using the victim's cookies; clickjacking tricks users into clicking inside an invisible iframe. Each has a distinct attack surface and a distinct defense: output encoding and CSP for XSS, SameSite cookies and tokens for CSRF, and `X-Frame-Options` / `frame-ancestors` for clickjacking.
Jump to the interview angleThree threats at a glance
| Attack | Vector | Primary defense | |
|---|---|---|---|
| Stored XSS | Malicious script saved to DB, served to all users | Output encode on render; strict CSP | |
| Reflected XSS | Script in URL echoed back in response | Output encode; avoid echoing raw query params | |
| DOM XSS | Client-side JS writes attacker-controlled data to the DOM | Avoid innerHTML; use DOMPurify or trusted types | |
| CSRF | Forged cross-origin request rides victim's session cookie | SameSite=Strict/Lax cookie; CSRF token or double-submit | |
| Clickjacking | Victim clicks UI element inside a hidden iframe | Content-Security-Policy: frame-ancestors 'none' |
DOM XSS: the dangerouslySetInnerHTML trap and DOMPurify fix
React's dangerouslySetInnerHTML bypasses its own escaping. Pass untrusted HTML through DOMPurify before setting it.
import DOMPurify from "dompurify";
// BAD — executes any script in userHtml
function UnsafeRichText({ userHtml }: { userHtml: string }) {
return <div dangerouslySetInnerHTML={{ __html: userHtml }} />;
}
// GOOD — DOMPurify strips event handlers and script tags
function SafeRichText({ userHtml }: { userHtml: string }) {
const clean = DOMPurify.sanitize(userHtml, {
USE_PROFILES: { html: true },
});
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}DOMPurify.sanitize removes <script>, onerror=, javascript: hrefs, and similar sinks. USE_PROFILES: { html: true } allows safe block/inline elements while stripping executable content.
Layered XSS defense
- 1
Output-encode on render
Escape
<,>,&,",'in any context where user data appears in HTML. Frameworks like React do this automatically for text nodes — the danger is only when you opt into raw HTML viadangerouslySetInnerHTMLorinnerHTML. - 2
Add a Content Security Policy
A
Content-Security-Policyheader tells the browser which script origins are allowed.script-src 'self'blocks inline scripts and third-party injections even if encoding fails. Start in report-only mode with areport-uriendpoint to catch violations before enforcing. - 3
Block dangerous sinks with Trusted Types
The Trusted Types API (Chromium-only) makes
innerHTML,eval, and script URLs accept only pre-approved wrapper objects, not raw strings. Enforce viarequire-trusted-types-for 'script'in CSP. Angular and Closure support it natively.
CSRF and clickjacking defenses
- **`SameSite=Strict`**: session cookie never sent on cross-site requests — strongest CSRF defense for same-domain apps.
- **`SameSite=Lax`** (browser default since 2020): blocks cross-site POST but allows top-level GET navigations.
- **CSRF tokens** (synchronizer token pattern) add a server-generated secret to forms; the server rejects requests missing it.
- **Double-submit cookie**: echo a secret in cookie and request header; server validates they match — no session store needed.
- **`frame-ancestors 'none'`** in CSP (or legacy `X-Frame-Options: DENY`) stops your page being embedded in an iframe, blocking clickjacking.
`SameSite=Lax` is not a complete CSRF defense
Lax blocks cross-site POST but allows cross-site GET. Any state-mutating GET endpoint is still vulnerable. Use Strict or pair Lax with a CSRF token. Also note: SameSite is a per-cookie attribute — it only applies if your API and front end share the same registrable domain (e.g., both on example.com).
Interview angle
Interviewers expect you to distinguish the three attacks by attack surface, not just name them. Cover why dangerouslySetInnerHTML is the React-specific XSS sink, why CSRF tokens matter even with SameSite, and why frame-ancestors in CSP supersedes X-Frame-Options.
Soundbite: "Encode output for XSS, set SameSite cookies for CSRF, and set frame-ancestors 'none' for clickjacking — three attacks, three headers."
Key terms
- XSS (Cross-Site Scripting)
- An injection attack where untrusted script executes in another user's browser under the victim site's origin.
- CSRF (Cross-Site Request Forgery)
- An attack that forges authenticated requests by exploiting the browser's automatic cookie attachment on cross-origin requests.
- SameSite cookie
- A cookie attribute (Strict/Lax/None) restricting when the browser includes the cookie on cross-site requests.
- Content Security Policy (CSP)
- An HTTP response header declaring allowed sources for scripts, styles, and frames; mitigates XSS and clickjacking.
- Trusted Types
- A browser API enforced via CSP that requires DOM sinks like innerHTML to accept typed wrapper objects, not raw strings.