Skip to content

CWE-83: XSS (Improper Neutralization)

Overview

Cross-Site Scripting (XSS) via improper neutralization of script-related HTML occurs when applications fail to properly encode, escape, or sanitize user-controlled data before including it in HTML output. This allows attackers to inject malicious scripts (typically JavaScript) that execute in victims' browsers within the security context of the vulnerable application. XSS attacks enable session hijacking, credential theft, phishing, keylogging, defacement, malware distribution, and unauthorized actions on behalf of authenticated users.

OWASP Classification

A05:2025 - Injection

Risk

High: XSS vulnerabilities can lead to complete account compromise through session token theft, credential harvesting via fake login forms, malware delivery, defacement, unauthorized transactions, and privilege escalation. Modern browsers have some built-in XSS protections, but these are insufficient against many attack vectors, especially DOM-based XSS.

Remediation Steps

Core principle: Never trust user input in HTML context; always apply context-appropriate output encoding (HTML entity encoding, JavaScript encoding, URL encoding, CSS encoding) based on where data appears in the page structure.

Identify the XSS Vulnerability Type

Determine which XSS variant exists:

  • Reflected XSS: User input immediately reflected in response (search results, error messages)
  • Stored XSS: Malicious input persisted in database and displayed to other users (comments, profile fields)
  • DOM-based XSS: Client-side JavaScript directly manipulates DOM with untrusted data
  • Mutation XSS (mXSS): Browser mutations create XSS from seemingly safe markup

Apply Context-Appropriate Output Encoding (Primary Defense)

HTML Body Context:

# VULNERABLE
html = f"<div>{user_input}</div>"

# SECURE - HTML entity encode
from html import escape
html = f"<div>{escape(user_input)}</div>"

HTML Attribute Context:

// VULNERABLE
div.setAttribute('title', userInput);

// SECURE - use DOM properties (automatically encodes)
div.title = userInput;

JavaScript Context:

// VULNERABLE
var msg = '<script>var x = "' + userInput + '";</script>';

// SECURE - JSON encode
var msg = '<script>var x = ' + JSON.stringify(userInput) + ';</script>';

URL Context:

# VULNERABLE
url = f"https://example.com?q={search_term}"

# SECURE - URL encode
from urllib.parse import quote
url = f"https://example.com?q={quote(search_term)}"

Use Framework Auto-Escaping Features

Modern frameworks provide automatic contextual escaping:

  • React/JSX: Auto-escapes by default in curly braces {userInput}
  • Django: {{ user_input }} auto-escapes (avoid {{ user_input|safe }})
  • Jinja2: {{ user_input }} auto-escapes (avoid {{ user_input|safe }})
  • Angular: Text interpolation {{userInput}} auto-escapes
  • Vue.js: Mustache syntax {{userInput}} auto-escapes (avoid v-html)

Content Security Policy (Defense in Depth)

Implement strict CSP headers:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'
  • Blocks inline scripts (<script>alert(1)</script>)
  • Prevents loading scripts from untrusted domains
  • Mitigates impact of XSS even if encoding fails

Input Validation (Defense in Depth)

Validate expected format:

  • Type validation: Numbers should be numeric, dates should match date format
  • Length limits: Prevent excessively long inputs
  • Allowlist: For enumerated values, only accept known-good values
  • Character restrictions: Reject <, >, ", ' when not needed

Important: Input validation alone is insufficient - always encode output.

Avoid Dangerous Patterns

Never use these with untrusted data:

  • innerHTML, outerHTML, document.write() with unencoded input
  • eval(), setTimeout(string), setInterval(string) with user data
  • javascript: URLs populated from user input
  • Unsafe framework features: dangerouslySetInnerHTML (React), v-html (Vue), |safe (Django/Jinja)

Test for XSS

Common XSS 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)

Dynamic Scan Guidance

For guidance on remediating this CWE when detected by dynamic (DAST) scanners:

Additional Resources