OWASP Web Top 10 — A03

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

1
Attacker Identifies Injection Point

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.

2
Malicious Payload Crafted

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')>.

3
Payload Delivered to Victim

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.

4
Malicious Script Executes

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.

5
Attacker Achieves Objective

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

VULNERABLE — Unencoded User Input
@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 cookies

Secure Code Example

SECURE — Output Encoding Applied
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: &lt;script&gt;alert(document.cookie)&lt;/script&gt;
// The script displays as text instead of executing

Types of Cross-Site Scripting

Reflected XSS

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>

Stored XSS

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.

DOM-Based XSS

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

Session Hijacking & Account Takeover

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.

Credential Theft & Phishing

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.

Website Defacement & Trust Damage

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.

Malware Distribution & Network Propagation

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

Context-Aware Output Encoding

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.

Implement Content Security Policy (CSP)

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.

Use HttpOnly and Secure Cookie Flags

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 and Sanitize Input

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.

Leverage Framework Auto-Escaping

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.

Regular Security Testing

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

2005

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.

2018

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.

2019

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.

2015-2016

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.