17 · Frontend Security
The threats a frontend engineer owns or influences: XSS, CSRF, the CSP/CORS defenses, auth and token handling (OAuth2/OIDC/PKCE), supply-chain risk, and the new RSC/Server-Actions attack surface. Current as of the memory+HttpOnly-cookie token pattern and the 2025 Next.js RSC CVE wave.
Positioning
The frontend is where untrusted input meets the user’s session and the user’s browser — which makes it a primary target. A senior engineer treats security as a threat-model-driven, defense-in-depth discipline: know what each attack does, which layer stops it, and where the new server-rendering era (RSC, Server Actions, BFFs) shifts the surface. You won’t run pentests, but you must not introduce the vulnerabilities, and you must configure the defenses correctly.
Foundations: the core web attacks
XSS — Cross-Site Scripting (the frontend’s #1 threat)
Attacker-controlled script executes in your page’s origin, with full access to the DOM, cookies readable by JS, and localStorage.
- Stored (persisted, e.g., a malicious comment), Reflected (echoed from the request), DOM-based (client-side sink like
innerHTML/evalfed untrusted data). - Defenses: contextual output encoding/escaping (React escapes text by default — a major reason JSX is safer than string templates); never
dangerouslySetInnerHTMLwith untrusted input (sanitize with DOMPurify if you must render HTML); avoid dangerous sinks (innerHTML,eval,new Function,javascript:URLs); validate/sanitize at input and output; and a strict Content Security Policy as the backstop.
CSRF — Cross-Site Request Forgery
A malicious site causes the user’s browser to make an authenticated request to your app using the automatically-sent cookie. Only affects cookie-based auth (not Authorization: Bearer headers, which other sites can’t set).
- Defenses: SameSite cookies (
Laxdefault,Strictfor sensitive) — the modern primary defense; anti-CSRF tokens (synchronizer/double-submit) for state-changing requests; verifyOrigin/Referer; require non-simple requests for mutations.
Clickjacking
Your page framed invisibly over a lure. Defenses: X-Frame-Options: DENY / CSP frame-ancestors 'none'.
Deep dive: the defenses
1. Content Security Policy (CSP)
An HTTP header that whitelists allowed sources for scripts, styles, images, connections, frames — the strongest structural defense against XSS.
- Prefer a nonce- or hash-based policy (
script-src 'nonce-...') over'unsafe-inline'; addstrict-dynamicfor modern script loading. Avoid'unsafe-inline'/'unsafe-eval'— they gut the policy. - Set
object-src 'none',base-uri 'none',frame-ancestors. Roll out with Report-Only + a reporting endpoint first, then enforce. - In Next, generate per-request nonces in middleware and thread them through (
08).
2. CORS — Cross-Origin Resource Sharing
A browser mechanism that relaxes the Same-Origin Policy, not a server-side defense. It controls which origins’ JS may read cross-origin responses, via Access-Control-Allow-Origin etc. Common senior points: CORS does not protect your API (anyone can call it directly with curl); never reflect arbitrary Origin into Allow-Origin with Allow-Credentials: true (that’s a serious misconfig); preflight (OPTIONS) is triggered by non-simple requests; CORS misconfig leaks data, it doesn’t “secure” anything.
3. Authentication & token handling (the part most often done wrong)
(This section is the security/token-storage angle; the OAuth/OIDC protocol, flows, sessions-vs-tokens, authz models, and passkeys are covered from zero in 26.)
- OAuth 2.0 / OIDC with the Authorization Code flow + PKCE is the standard for browser apps (OAuth 2.1 makes PKCE mandatory and drops the implicit flow). PKCE secures token acquisition (intercept protection), not token storage — a frequently-missed distinction.
- Where to put tokens (2026 consensus): short-lived access token in memory (JS variable/state — gone on refresh, re-obtained via silent refresh) + refresh token in an
HttpOnly,Secure,SameSitecookie (unreadable by JS). This hybrid resists XSS (no token inlocalStoragefor a script to steal) and CSRF (the access token isn’t auto-sent; the refresh cookie isSameSite). - Avoid
localStoragefor tokens — fully readable by any XSS. This is the single most common auth mistake. - JWT hygiene: verify signature server-side, check
exp/aud/iss, keep access tokens short-lived, rotate refresh tokens, and remember JWTs can’t be easily revoked (use short lifetimes + a revocation/denylist for refresh). - Session cookies (server sessions) remain a perfectly good, often simpler choice —
HttpOnly; Secure; SameSite=Lax.
4. Supply-chain security (rising threat)
Your app is mostly other people’s code. Risks: malicious/compromised npm packages, typosquatting, dependency confusion, compromised maintainers, malicious post-install scripts.
- Defenses: lockfiles +
npm ci;npm audit/Snyk/Dependabot; pin and review dependencies; minimize dependency count; Subresource Integrity (SRI) for CDN scripts; verify provenance (npm provenance/sigstore); be wary of post-install scripts; an SBOM for enterprise. Treat a new dependency as new attack surface.
5. The RSC / Server-Actions / BFF surface (the 2025–26 shift)
Server-side rendering and server actions move code execution and trust boundaries:
- Server Actions are public POST endpoints (
08,12) — they look like function calls but are network-exposed. Always validate input (Zod) and authorize inside every action; never assume the UI is the only caller. - Don’t leak secrets to the client — env vars without the public prefix stay server-side; be careful what RSC serializes into the payload (props sent to client components are visible).
- The 2025 Next.js CVE wave underscored RSC-era risk: a critical RCE in RSC handling, plus DoS, source-code exposure, middleware auth-bypass, and cache-poisoning issues. Lesson: keep the framework patched aggressively, don’t put authorization solely in middleware, and treat RSC/route handlers as a real server with a real attack surface.
- SSR injection: data interpolated into server-rendered HTML must be encoded just like client output.
6. Other essentials
- HTTPS everywhere + HSTS; Secure cookies.
- Security headers: CSP,
X-Content-Type-Options: nosniff,Referrer-Policy,Permissions-Policy,frame-ancestors. - Open redirects, prototype pollution, ReDoS, and sensitive data in URLs/logs — know them.
- Input validation at every trust boundary (
12) — the client is never authoritative; the server re-validates.
Worked example: the memory + HttpOnly-cookie auth pattern
Login (OAuth2.1 Authorization Code + PKCE)
client ──code+verifier──▶ /token (server)
server ─ Set-Cookie: refresh=...; HttpOnly; Secure; SameSite=Strict; Path=/auth
─ returns access token in body ─▶ client keeps it in MEMORY only
Each API call: Authorization: Bearer <access-in-memory> (not auto-sent → CSRF-safe)
On 401 / refresh: POST /auth/refresh (browser auto-sends HttpOnly refresh cookie)
→ new short-lived access token (rotate refresh)
On page reload: access token is gone → silent /auth/refresh re-obtains it
Why it’s robust: an XSS can’t read an HttpOnly cookie, and there’s no token in localStorage to steal; the bearer access token isn’t auto-attached cross-site, so CSRF doesn’t apply to it; the refresh cookie is SameSite. Layer a strict CSP on top to shrink the XSS surface that makes any token theft possible in the first place.
Pitfalls & gotchas
- Tokens in
localStorage— XSS-readable; the classic mistake. dangerouslySetInnerHTMLwith untrusted HTML and no sanitization → stored XSS.- Thinking CORS secures your API — it controls browser reads, nothing else; the API still needs auth.
- Reflecting
Origin+Allow-Credentials: true— credential-leaking CORS misconfig. - Server Actions / route handlers without input validation + authorization — public endpoints treated as trusted UI calls.
- Authorization only in middleware (Next CVE lesson) — enforce in the handler/action too.
- Secrets serialized into RSC/client props or public env vars.
'unsafe-inline'/'unsafe-eval'in CSP — defeats the policy.- Unpatched framework in the RSC-CVE era — patch fast.
- Trusting client-side validation — always re-validate server-side.
Interview questions
- XSS types and defenses? Why is React safer by default, and where does it stop protecting you?
- What is CSRF, which auth schemes are vulnerable, and how does SameSite help?
- Is CORS a security mechanism? What does it actually control?
- Where should you store access vs refresh tokens, and why?
- What does PKCE protect — acquisition or storage?
- How do you design a CSP that actually mitigates XSS?
- Why are Server Actions a security concern, and how do you secure them?
- Summarize the 2025 Next.js RSC CVE lessons.
- How do you defend against supply-chain attacks in npm?
- Why isn’t client-side validation sufficient?
Recommendations
- Access token in memory, refresh token in
HttpOnly; Secure; SameSitecookie; never tokens inlocalStorage. Use OAuth2.1 Auth Code + PKCE / OIDC. - Deploy a strict, nonce-based CSP (Report-Only → enforce) plus the full security-header set.
- Escape/sanitize all output; ban untrusted
dangerouslySetInnerHTML; avoid dangerous sinks. - Validate + authorize inside every Server Action / route handler / BFF endpoint (
12); don’t rely on middleware alone. - Keep secrets server-side; audit what RSC serializes to the client.
- Lock the supply chain:
npm ci, audit/Dependabot/Snyk, SRI, minimal deps, watch post-install scripts. - Patch frameworks aggressively — the RSC era has real CVEs.
- Threat-model features; re-validate everything server-side.
Books & references
- OWASP — the canonical source: OWASP Top 10, the Cheat Sheet Series (XSS Prevention, CSRF Prevention, CSP, JWT, Session Management), and ASVS. Start here.
- “OAuth 2.0 for Browser-Based Apps” (IETF draft/BCP) — the authoritative token-storage and PKCE guidance for SPAs.
- “The Tangled Web” — Michał Zalewski. Deep browser-security-model classic.
- “Web Application Security” — Andrew Hoffman (O’Reilly). Modern, practical, frontend-aware.
- web.dev/secure & MDN Web Security — CSP, CORS, SameSite, headers, with current examples.
- Next.js security docs + GitHub Security Advisories — RSC/Server-Actions guidance and the CVE history; subscribe to advisories.
- PortSwigger Web Security Academy — free, hands-on labs for XSS/CSRF/CORS/auth.
Connections
26-authentication-and-authorization.md— the OAuth/OIDC protocol, flows, sessions-vs-tokens, authz models, and passkeys from zero; this file is its security/threat half.04-the-web-platform.md— cookies, storage, the DOM sinks, Same-Origin Policy that these attacks/defenses revolve around.08-nextjs-and-meta-frameworks.md— Server Actions, middleware, env handling, and the RSC CVEs.12-bff-and-data-enrichment.md— the BFF keeps secrets server-side and is an auth/validation choke point.02-javascript-deep-dive.md—eval/Function, prototype pollution, dangerous dynamic patterns.14-build-tools-and-bundlers.md— supply chain, SRI, what ends up in the bundle.18-networking-and-protocols.md— HTTPS/HSTS/TLS and security headers at the transport layer.