Frontend Security: XSS, CSRF, CORS and Content Security Policy

Frontend security explained: XSS and how to prevent it, CSRF and SameSite cookies, CORS, Content Security Policy and storing tokens safely in the browser.

Intermediate 3 min read · Updated

Frontend security protects users from attacks that run in or through their browser. The big four are cross-site scripting (XSS), where an attacker's script runs on your page; cross-site request forgery (CSRF), where another site makes the user's browser perform actions on yours; misconfigured CORS, which can expose APIs to other origins; and weak defences such as missing Content Security Policy or tokens stored where scripts can steal them.

blockedtrusted onlycookie + tokenforged POSTrejectedInjected scriptXSSCSPscript-src selfYour pageYour APIOther site's formCSRFSameSite + token
Browser defences in layers: a Content Security Policy stops injected scripts, and SameSite cookies plus a CSRF token stop forged requests from other sites.

Cross-site scripting (XSS)

XSS happens when untrusted data is inserted into a page in a way the browser executes as code. A comment containing <img src=x onerror="steal(document.cookie)"> that's rendered as raw HTML runs in every viewer's session, with full access to the page and its data.

TypeHowExample
StoredMalicious input is saved and shown to other usersA comment, profile name or product review
ReflectedInput from the URL is echoed into the pageA search page that prints the query unescaped
DOM-basedClient-side code writes untrusted data into the DOMelement.innerHTML = location.hash
  • Encode output by context: HTML-escape text, attribute-encode attributes, and never build scripts or URLs from raw input. Modern frameworks escape text by default; the danger is the escape hatches (dangerouslySetInnerHTML, v-html, innerHTML).
  • Sanitise any HTML you must render (rich text, markdown) with a well-maintained allow-list sanitiser.
  • Validate URLs before using them in links or redirects; reject javascript: and other dangerous schemes.
  • Content Security Policy (below) as a second line of defence.

Content Security Policy (CSP)

CSP is a response header that tells the browser which sources of scripts, styles, images and connections are allowed. Even if an attacker injects markup, the browser refuses to run inline scripts or load scripts from unapproved origins.

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-r4nd0m';
  object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests
  • Avoid 'unsafe-inline' and 'unsafe-eval' for scripts; use nonces or hashes for the few inline scripts you need.
  • frame-ancestors prevents your site from being framed by others (clickjacking).
  • Roll out with Content-Security-Policy-Report-Only first to find violations without breaking the site.

Cross-site request forgery (CSRF)

If a site authenticates with cookies, a malicious page can make the user's browser send a request to it (a form auto-submitting to /transfer), and the browser attaches the cookies automatically. Defences:

  • SameSite cookies: SameSite=Lax (a common default) or Strict stops cookies being sent on most cross-site requests.
  • CSRF tokens: a per-session secret the server requires in a header or form field, which other sites can't read.
  • Check Origin or Referer headers on state-changing requests.
  • Never change state on GET requests.

CORS

The same-origin policy stops scripts on one origin from reading responses from another. CORS (cross-origin resource sharing) lets a server relax that for specific origins with headers such as Access-Control-Allow-Origin. It's a relaxation, not a protection: the danger is misconfiguring it, for example reflecting any request's Origin while also allowing credentials, which lets any site read users' private data from your API. Allow-list exact origins, and only allow credentials where genuinely needed.

Storing tokens in the browser

StorageXSS can steal it?CSRF risk?Notes
HttpOnly, Secure, SameSite cookieNo (scripts can't read it)Mitigated by SameSite and tokensThe usual recommendation for session credentials
localStorage / sessionStorageYesNoAny XSS can exfiltrate long-lived tokens
In memory onlyHarder, but XSS can still act as the userNoLost on reload; paired with a refresh cookie

No storage choice survives XSS completely: injected script can always act as the user while the page is open. Preventing XSS comes first; token storage limits how much damage it does afterwards. See authentication.

Other essentials

  • HTTPS everywhere with HSTS. See encryption.
  • Subresource Integrity hashes for scripts loaded from third-party CDNs.
  • Dependency hygiene: audit and pin packages; a compromised dependency runs with your page's privileges.
  • Never trust the client: validate and authorise everything on the server; client checks are for UX only.
  • Don't put secrets in frontend code: anything shipped to the browser is public.

Where it’s used

Social and content apps

Sanitised rich text and a strict CSP limit stored XSS from user content.

Banking and payments

HttpOnly, SameSite session cookies with CSRF tokens protect state-changing requests.

Public APIs

CORS allow-lists only first-party origins for credentialed requests.

In an interview

  • Explain stored, reflected and DOM XSS, and prevent them with output encoding and sanitisation.
  • Add a nonce-based CSP as defence in depth.
  • Prevent CSRF with SameSite cookies and tokens, and never mutate on GET.
  • Configure CORS with exact origins and store session tokens in HttpOnly cookies.

Further reading