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.
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.
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.
| Type | How | Example |
|---|---|---|
| Stored | Malicious input is saved and shown to other users | A comment, profile name or product review |
| Reflected | Input from the URL is echoed into the page | A search page that prints the query unescaped |
| DOM-based | Client-side code writes untrusted data into the DOM | element.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-ancestorsprevents your site from being framed by others (clickjacking).- Roll out with
Content-Security-Policy-Report-Onlyfirst 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) orStrictstops 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
| Storage | XSS can steal it? | CSRF risk? | Notes |
|---|---|---|---|
| HttpOnly, Secure, SameSite cookie | No (scripts can't read it) | Mitigated by SameSite and tokens | The usual recommendation for session credentials |
| localStorage / sessionStorage | Yes | No | Any XSS can exfiltrate long-lived tokens |
| In memory only | Harder, but XSS can still act as the user | No | Lost 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
Sanitised rich text and a strict CSP limit stored XSS from user content.
HttpOnly, SameSite session cookies with CSRF tokens protect state-changing requests.
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
- Cross Site Scripting Prevention Cheat SheetOWASP Foundation
- Content Security Policy (CSP)MDN Web Docs (Mozilla)