Skip to content
Frontend Security

Cross-cutting Quality

Listen 0%
Speed

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/eval fed untrusted data).
  • Defenses: contextual output encoding/escaping (React escapes text by default — a major reason JSX is safer than string templates); never dangerouslySetInnerHTML with 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 (Lax default, Strict for sensitive) — the modern primary defense; anti-CSRF tokens (synchronizer/double-submit) for state-changing requests; verify Origin/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'; add strict-dynamic for 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, SameSite cookie (unreadable by JS). This hybrid resists XSS (no token in localStorage for a script to steal) and CSRF (the access token isn’t auto-sent; the refresh cookie is SameSite).
  • Avoid localStorage for 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.

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.
  • dangerouslySetInnerHTML with 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

  1. XSS types and defenses? Why is React safer by default, and where does it stop protecting you?
  2. What is CSRF, which auth schemes are vulnerable, and how does SameSite help?
  3. Is CORS a security mechanism? What does it actually control?
  4. Where should you store access vs refresh tokens, and why?
  5. What does PKCE protect — acquisition or storage?
  6. How do you design a CSP that actually mitigates XSS?
  7. Why are Server Actions a security concern, and how do you secure them?
  8. Summarize the 2025 Next.js RSC CVE lessons.
  9. How do you defend against supply-chain attacks in npm?
  10. Why isn’t client-side validation sufficient?

Recommendations

  • Access token in memory, refresh token in HttpOnly; Secure; SameSite cookie; never tokens in localStorage. 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

Frontend Deep-Dive Library · content is the single source of truth.