Understanding and Preventing Cross-Site Scripting (XSS)
A practical guide to identifying, exploiting, and defending against DOM-based XSS vulnerabilities in modern web applications.
Cross-Site Scripting (XSS) remains one of the most common web application vulnerabilities. It allows attackers to inject malicious scripts into web pages viewed by other users, potentially stealing cookies, session tokens, or other sensitive information.
What is XSS?
XSS occurs when an application includes untrusted data in a web page without proper validation or escaping. There are three main types:
- Stored XSS, The malicious script is permanently stored on the target server (e.g., in a database).
- Reflected XSS, The script is reflected off a web server in error messages, search results, or any response that includes input from a user.
- DOM-based XSS, The vulnerability exists entirely in client-side code. The attack payload is executed by modifying the DOM in the victim's browser.
A Simple Example
Consider a search page that displays the user's query:
// Vulnerable code, DO NOT USE
document.getElementById('results').innerHTML =
'You searched for: ' + userInput;
If userInput contains <img src=x onerror=alert('XSS')>, the browser will execute the attacker's script.
Prevention Techniques
1. Escape Output
Always escape user input before rendering it in HTML. Use textContent instead of innerHTML when inserting plain text:
// Safe approach
document.getElementById('results').textContent =
'You searched for: ' + userInput;
2. Content Security Policy (CSP)
A CSP header restricts the sources from which scripts can be loaded. This prevents inline scripts from executing even if an attacker injects them.
Content-Security-Policy: default-src 'self'; script-src 'self'
3. Input Validation
Validate and sanitize all user inputs on both client and server side. Use allowlists for expected formats (e.g., email, numbers) rather than blocklists.
4. Use a Sanitization Library
When you must render user-provided HTML, use libraries like DOMPurify to strip dangerous elements and attributes:
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirtyHTML);
element.innerHTML = clean;
Key Takeaways
- Never trust user input, always validate and sanitize.
- Prefer
textContentoverinnerHTML. - Implement Content Security Policy headers.
- Use established sanitization libraries when rendering HTML.
- Regularly test your application with tools like Burp Suite or OWASP ZAP.