Frontend Security (XSS, CSRF, CSP, Input Sanitization)
The UI layer is the first line of defense: escape output, sanitize HTML with DOMPurify, lock down resources with CSP, and protect requests with CSRF tokens and SameSite cookies.
Frontend engineers are the first line of defense against common web attacks. You don't need to be a security specialist, but you must prevent the vulnerabilities that originate in the UI layer — most of them come down to never trusting input and never injecting untrusted strings into the page.
XSS (Cross-Site Scripting) is the headline threat: an attacker injects a malicious script into your page so it runs in the victim's browser with their privileges. React escapes JSX by default, so {userInput} renders as text, not markup. The danger zone is dangerouslySetInnerHTML — only use it on HTML you have sanitized first with a library like DOMPurify.
CSRF (Cross-Site Request Forgery) tricks an authenticated user into firing an unwanted request to your server (the browser auto-attaches their cookies). Defend with anti-CSRF tokens, the SameSite cookie attribute, and verifying the Origin header on the server. Clickjacking overlays an invisible iframe over your page; block it with X-Frame-Options: DENY or frame-ancestors in CSP. Open redirects abuse your URLs to bounce users to malicious sites — always validate and whitelist redirect targets.
Here are the core attacks and their fixes:
| Attack | What It Is | Prevention |
|---|---|---|
| XSS (Cross-Site Scripting) | Attacker injects malicious scripts into your page | React escapes JSX by default; never use dangerouslySetInnerHTML; sanitize user input with DOMPurify |
| CSRF (Cross-Site Request Forgery) | Attacker tricks user into making unwanted requests | Use CSRF tokens; SameSite cookie attribute; verify Origin header on server |
| Clickjacking | Attacker overlays invisible iframe over your page | X-Frame-Options: DENY header; frame-ancestors in CSP |
| Open Redirect | Attacker uses your URL to redirect to malicious site | Validate redirect URLs; whitelist allowed domains |
A Content Security Policy (CSP) is your defense-in-depth backstop: an HTTP header (or meta tag) that whitelists exactly which origins the browser may load scripts, styles, images, and connections from, so even an injected script is blocked from executing or phoning home.
Input sanitization on the frontend is the same discipline as parameterized queries and bean validation on the backend — never concatenate untrusted input into something that gets interpreted (HTML in the browser, SQL in the database). CSP is like a firewall allowlist or an outbound egress policy: explicitly enumerate the origins you trust and deny everything else. CSRF tokens mirror the synchronizer-token pattern you'd enforce server-side.
- React auto-escapes JSX, so {userInput} is safe by default; the risk appears the moment you reach for dangerouslySetInnerHTML.
- Never inject raw user HTML — run it through DOMPurify.sanitize() first, then pass the cleaned string.
- Defend CSRF with SameSite cookies + anti-CSRF tokens + server-side Origin verification; defend clickjacking with frame-ancestors / X-Frame-Options.
- CSP is defense-in-depth: a whitelist of allowed resource origins that stops injected scripts from running or exfiltrating data.
Worked Code
<!-- Content Security Policy (CSP) - set via HTTP header or meta tag -->
<!-- Restricts what resources the browser can load -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
" />// DOMPurify — sanitize user-generated HTML
import DOMPurify from 'dompurify';
function UserComment({ htmlContent }: { htmlContent: string }) {
// NEVER do this with raw user input
// <div dangerouslySetInnerHTML={{ __html: htmlContent }} />
// Sanitize first
const clean = DOMPurify.sanitize(htmlContent);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}
// React auto-escapes by default — this is SAFE
function Safe({ userInput }: { userInput: string }) {
return <p>{userInput}</p>; // <script> tags rendered as text, not executed
}Interview-Ready Q&A
XSS (Cross-Site Scripting) is when an attacker injects a script that runs in the victim's browser. The main types are stored (persisted in your DB and served to every visitor), reflected (echoed back from a request, e.g. a search query), and DOM-based (the injection happens entirely client-side via unsafe DOM APIs). React mitigates it by escaping JSX by default, so {userInput} renders as text. Prevention: avoid dangerouslySetInnerHTML, and when you must render HTML, sanitize it with DOMPurify; also set a strong CSP as a backstop.
- 1XSS: React escapes JSX by default; never inject raw user HTML — sanitize with DOMPurify before dangerouslySetInnerHTML.
- 2CSRF: SameSite cookies + anti-CSRF tokens + server-side Origin verification.
- 3CSP is a resource allowlist (defense-in-depth); frame-ancestors / X-Frame-Options stop clickjacking, and redirect URLs must be whitelisted.