Cross-Site Scripting (XSS)
Cross-Site Scripting allows attackers to inject malicious scripts into web pages viewed by other users. Learn how XSS works, the different attack types, and proven prevention techniques to protect your applications.
What Is Cross-Site Scripting?
Cross-Site Scripting (XSS) is a client-side code injection attack where an attacker can execute malicious scripts in a victim's browser by including malicious code in a legitimate web page or application. When a user visits the compromised page, the malicious script executes in their browser with the same privileges as legitimate scripts from that website.
XSS vulnerabilities occur when an application includes untrusted data in a web page without proper validation or escaping, or updates existing page content using browser APIs that can create HTML or JavaScript. This allows attackers to steal session tokens, redirect users to malicious sites, deface websites, or deliver malware.
Despite being well-understood for over two decades, XSS remains one of the most prevalent web application security flaws. According to the OWASP Top 10, injection flaws including XSS consistently rank among the most critical security risks, affecting applications across all industries and technology stacks.
How XSS Attacks Work
The attacker discovers a location where user input is reflected in the page without proper encoding, such as search results, profile fields, comment sections, or URL parameters.
The attacker creates a payload containing JavaScript code, often disguised within HTML tags: <script>alert('XSS')</script> or using event handlers like <img src=x onerror=alert('XSS')>.
For reflected XSS, the attacker tricks the victim into clicking a malicious link. For stored XSS, the payload is saved in the application's database. For DOM-based XSS, the payload manipulates the client-side code.
The victim's browser renders the page and executes the injected script. The script runs with the same origin and permissions as legitimate scripts from that domain, giving it access to cookies, session tokens, and page content.
The malicious script performs actions such as stealing session cookies (document.cookie), capturing keystrokes, redirecting to phishing sites, or modifying page content to trick users into revealing sensitive information.
Vulnerable Code Example
@WebServlet("/search")
public class SearchServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String query = request.getParameter("q");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// VULNERABLE: User input directly written to response without encoding
out.write("<html><body>");
out.write("<h1>Search Results</h1>");
out.write("<p>Results for: " + query + "</p>");
out.write("</body></html>");
}
}
// Attack URL: /search?q=<script>alert(document.cookie)</script>
// The script executes in the victim's browser, exposing session cookiesSecure Code Example
import org.owasp.encoder.Encode;
@WebServlet("/search")
public class SearchServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String query = request.getParameter("q");
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("<html><body>");
out.write("<h1>Search Results</h1>");
// SECURE: HTML-encode user input before rendering
out.write("<p>Results for: " + Encode.forHtml(query) + "</p>");
out.write("</body></html>");
}
}
// Attack attempt: /search?q=<script>alert(document.cookie)</script>
// Output: Results for: <script>alert(document.cookie)</script>
// The script displays as text instead of executingTypes of Cross-Site Scripting
Non-persistent attacks where malicious scripts are reflected off a web server, such as in search results, error messages, or any response that includes user-supplied input. The attacker must trick the victim into clicking a specially crafted link. Example: /search?q=<script>steal()</script>
Persistent attacks where the malicious script is permanently stored on the target server (in databases, comment fields, user profiles, or forums). Every user who views the infected content becomes a victim. This is the most dangerous type as it requires no user interaction beyond visiting the page.
Client-side attacks where the vulnerability exists in client-side code rather than server-side. The malicious payload is executed as a result of modifying the DOM environment. Common with JavaScript frameworks that use innerHTML, eval(), or manipulate the URL hash without proper sanitization.
Impact of XSS Attacks
Attackers steal session cookies using document.cookie to impersonate users without knowing their credentials. This grants full access to the victim's account, allowing unauthorized transactions, data modification, or privilege escalation.
Malicious scripts can create fake login forms overlay on legitimate pages, capturing usernames and passwords. Keyloggers can be injected to record everything the user types, including sensitive information like credit card numbers and personal data.
XSS can modify page content, inject fake news or warnings, redirect users to competitor sites, or display offensive material. This destroys brand reputation and user trust, potentially causing permanent customer loss and legal liability.
Injected scripts can force-download malware, exploit browser vulnerabilities, or create self-propagating XSS worms that automatically inject themselves into other users' content, as demonstrated by the Samy worm that infected over 1 million MySpace profiles in 24 hours.
XSS Prevention Checklist
Encode all user-controlled data based on where it's placed in the HTML document (HTML entity encoding for HTML body, JavaScript encoding for scripts, URL encoding for URLs, CSS encoding for styles). Use established libraries like OWASP Java Encoder or DOMPurify.
Deploy strict CSP headers to restrict which scripts can execute. Use nonces or hashes for inline scripts, disable unsafe-inline and unsafe-eval, and whitelist only trusted domains. CSP acts as a defense-in-depth layer even if encoding fails.
Set HttpOnly flag on session cookies to prevent JavaScript access via document.cookie. Add Secure flag to ensure cookies are only transmitted over HTTPS. This limits the damage even if XSS occurs, as attackers cannot steal session tokens directly.
Validate all input against expected formats using allowlists (not denylists). Reject unexpected input rather than trying to sanitize it. For rich content, use proven sanitization libraries that parse and clean HTML while removing dangerous elements and attributes.
Modern frameworks like React, Vue, and Angular automatically escape content by default. Use these built-in protections and avoid bypassing them with dangerous methods like dangerouslySetInnerHTML, v-html, or bypassSecurityTrust unless absolutely necessary with sanitized content.
Perform automated scanning with tools like Burp Suite, OWASP ZAP, or Acunetix. Conduct manual penetration testing focusing on all input points. Implement security code reviews and train developers on XSS patterns and prevention techniques.
Real-World XSS Breaches
Samy Worm / MySpace
A stored XSS worm created by Samy Kamkar that exploited MySpace's profile page. The worm automatically added Samy as a friend and replicated itself to viewer's profiles, infecting over 1 million users in under 24 hours before MySpace was forced offline.
British Airways
Attackers injected malicious JavaScript into BA's payment page through a compromised third-party script. The XSS payload captured customer payment card details and personal information from approximately 380,000 transactions, resulting in a £20 million GDPR fine.
Fortnite / Epic Games
Security researchers discovered XSS vulnerabilities in Epic Games' authentication flow and subdomain that could lead to account takeover. Attackers could steal V-Bucks, access player conversations, and make unauthorized purchases without user credentials.
eBay Persistent XSS
Multiple stored XSS vulnerabilities were discovered in eBay's listing descriptions and user profiles. Attackers could inject malicious scripts that executed when other users viewed the listings, potentially stealing credentials and hijacking sessions of millions of users.
Practice XSS Prevention
Learn to identify and fix XSS vulnerabilities with hands-on challenges. Master output encoding, CSP implementation, and secure coding patterns across multiple languages.