Skip to content

Web Security: XSS, CSRF, CORS, SOP#

Problem statement (interviewer prompt)

A web app on app.example.com calls APIs on api.example.com and embeds third-party widgets. Explain how the browser's Same-Origin Policy, CORS, and Content Security Policy combine to prevent classic web attacks (XSS, CSRF, clickjacking).

Browsers enforce same-origin policy: scripts from one origin can't read responses from another. CORS is the explicit relaxation; CSP is the explicit lockdown. CSRF and XSS are the attacks each layer addresses.

flowchart TB
  Browser[Browser] -->|SOP enforced| Origins[Origin = scheme + host + port]
  Origins --> CORS[CORS<br/>server opt-in for cross-origin]
  Origins --> CSP[CSP<br/>limit allowed scripts, sources]
  Browser --> XSS[XSS<br/>attacker JS in page]
  Browser --> CSRF[CSRF<br/>cross-site state-changing requests]
  XSS -. mitigated by .-> CSP
  CSRF -. mitigated by .-> SameSite[SameSite cookies + token]

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class Browser client;
    class CORS,CSP,SameSite edge;
    class Origins,XSS,CSRF service;

These are not abstract; misconfigure any one and you ship an exploitable app.

The browser's security model is the application's first line of defence. Misunderstanding it is the most common source of web vulns.

Same-Origin Policy#

Origin = scheme + host + port
https://a.example.com         != https://b.example.com  (host)
https://a.example.com:443     != https://a.example.com:8080  (port)
http://a.example.com          != https://a.example.com  (scheme)

Scripts from origin A cannot read responses from origin B by default. They can still trigger requests (forms, images, links) - that's why CSRF exists.

CORS - relaxing SOP#

The server explicitly allows cross-origin reads:

Request:
  Origin: https://app.example.com

Response:
  Access-Control-Allow-Origin: https://app.example.com
  Access-Control-Allow-Credentials: true
  Access-Control-Allow-Methods: GET, POST
  Access-Control-Allow-Headers: Authorization, Content-Type

For "non-simple" requests (custom headers, JSON, methods other than GET/HEAD/POST), the browser sends a OPTIONS preflight first.

sequenceDiagram
  participant B as Browser
  participant S as api.example.com
  B->>S: OPTIONS /resource (Origin, Access-Control-Request-Method, ...)
  S->>B: 204 + Access-Control-Allow-* headers
  B->>S: GET /resource
  S->>B: 200 + Access-Control-Allow-Origin

Pitfalls: - Access-Control-Allow-Origin: * cannot combine with credentials. - Echoing arbitrary Origin is a vulnerability when combined with credentials. - CORS is browser-side; non-browser clients ignore it.

XSS - cross-site scripting#

Attacker injects JavaScript into a page; it runs in the victim's session.

Variant Description
Reflected Input echoed in response: ?q=<script>
Stored Input persisted, served to others (comments, names)
DOM-based Sink in client JS: innerHTML = location.hash

Mitigations:

  1. Context-aware output encoding: HTML, attribute, JS, URL contexts each escape differently.
  2. Template engines that auto-escape (Jinja, Handlebars, React JSX).
  3. Content Security Policy:

    Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'; object-src 'none'
    
    Blocks inline scripts unless they carry the matching nonce. Tightens what <script> can run.

  4. Trusted Types API: forces all DOM XSS sinks through a typing function.

CSRF - cross-site request forgery#

An attacker tricks the victim's browser into making a state-changing request to a site they're logged into:

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

The browser auto-attaches the bank's session cookies; the bank can't tell apart legitimate vs forged requests.

Mitigations:

  1. SameSite cookies: SameSite=Lax (default in modern browsers) blocks cookies on cross-site requests. Strict is even safer.
  2. CSRF tokens: per-session random token in a header or hidden field; server verifies on every state-changing request.
  3. Double-submit cookie: same token in cookie and request body; server checks match.
  4. Origin/Referer header check: reject if Origin not in allowlist.
  5. SOP-bound APIs: APIs that only accept JSON with custom headers can't be triggered by classical CSRF (preflight required).

Clickjacking#

Attacker iframes your site over an invisible UI; tricks user into clicking what they don't see.

Mitigation: Content-Security-Policy: frame-ancestors 'self' or the older X-Frame-Options: DENY.

Modern security headers#

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Content-Security-Policy: ...
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=()

Use securityheaders.com to grade.

Cookies#

Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=86400
  • HttpOnly: JS can't read - mitigates XSS-driven session theft.
  • Secure: HTTPS only.
  • SameSite: Lax / Strict / None (with Secure).
  • __Host- / __Secure- prefixes give extra guarantees.

Subresource Integrity#

When loading scripts from a CDN, bind to a hash:

<script src="https://cdn.example/lib.js"
        integrity="sha384-..."
        crossorigin="anonymous"></script>

If the CDN is compromised and the file changes, the hash mismatches and the script is rejected.

Testing#

  • ZAP / Burp for dynamic scanning.
  • ESLint + eslint-plugin-security for static.
  • CSP-Evaluator (Google) for policy review.

How web security fits#

flowchart TB
  WEB((Web<br/>security))
  OWASP[OWASP Top 10<br/>broader categorisation]
  SEC[Security fundamentals<br/>AuthN/AuthZ parent]
  JWT[JWT internals<br/>bearer-token sibling]
  TLS[TLS / mTLS<br/>transport layer]
  OWASP --> WEB
  SEC --> WEB
  WEB -. complements .- JWT
  TLS --> WEB

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class WEB service;
    class OWASP,SEC,JWT,TLS datastore;

Glossary & fundamentals#

Tag Concept What it is Page
HLD OWASP Top 10 A01, A02, A03 cover most of this owasp-top-10
HLD Security fundamentals parent topic security-fundamentals
HLD TLS mTLS the transport encryption layer tls-mtls
HLD JWT internals bearer-token alternative to sessions jwt-internals

Quick reference#

Same-Origin Policy#

Origin = scheme + host + port. Different origin = no cross-origin reads.

CORS#

Server opts in to cross-origin reads via Access-Control-Allow-* headers. Non-simple requests trigger OPTIONS preflight.

Don't: Allow-Origin: * + credentials. Don't echo arbitrary Origin.

XSS#

Variant Defense
Reflected Output encoding
Stored Encode + CSP
DOM-based Trusted Types, safe sinks

CSP example:

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-xyz'; object-src 'none'

CSRF#

Defenses: - SameSite=Lax cookie (default browser) - Anti-CSRF token in header or hidden field - Double-submit cookie - Check Origin/Referer - JSON-only API + custom header → forces preflight

  • HttpOnly, Secure, SameSite=Lax
  • __Host- prefix for tightest scope

Modern headers#

  • HSTS
  • CSP
  • X-Content-Type-Options: nosniff
  • X-Frame-Options or frame-ancestors
  • Referrer-Policy
  • Permissions-Policy

Tools#

  • ZAP / Burp - DAST
  • CSP-Evaluator
  • securityheaders.com

Refs#

  • MDN SOP, CORS
  • OWASP XSS / CSRF Cheat Sheets

FAQ#

What is the same-origin policy?#

A browser rule that blocks scripts on one origin from reading responses or DOM content from another origin. It is the foundation of web security and the reason CORS exists.

How does CORS work?#

The server opts in by sending Access-Control-Allow-Origin and related headers. For non-simple requests, the browser sends a preflight OPTIONS request to check the policy before the real call.

What is the difference between CSRF and XSS?#

CSRF tricks an authenticated user into submitting an unwanted request. XSS executes attacker JavaScript in the victim's browser. CSRF is solved by tokens and SameSite cookies; XSS is solved by escaping and CSP.

How does Content Security Policy help?#

CSP restricts which sources of scripts, styles, images, and frames the browser will load. It is a defense-in-depth layer that limits damage even if an XSS vulnerability slips into the app.

It controls whether a cookie is sent on cross-site requests. SameSite=Lax or Strict blocks most CSRF vectors because the session cookie no longer travels with attacker-initiated requests.

Further reading#