CWE-83: XSS (Improper Neutralization)
Overview
This guidance helps you interpret and remediate findings from DAST (Dynamic Application Security Testing) tools. The scanner detected that the application performed insufficient or context-inappropriate neutralization of user input before placing it in HTML output, allowing XSS attacks to bypass weak filters or exploit incorrect encoding. DAST tools identify this by:
Detection Methods:
- Filter Bypass Testing: Submitting payloads designed to evade common filters:
- Tag nesting:
<scr<script>ipt>alert(1)</scr</script>ipt> - Case variation:
<ScRiPt>alert(1)</sCrIpT> - Alternative encodings: HTML entities (
<script>), URL encoding (%3Cscript%3E), Unicode (\u003cscript\u003e) - Null byte injection:
<script%00>alert(1)</script>
- Tag nesting:
- Context-Specific Bypasses:
- Attribute context:
" onload="alert(1),' onerror='alert(1)',javascript:alert(1)inhref/src - JavaScript context: Breaking out of strings (
'-alert(1)-',"; alert(1)//) - CSS context:
expression(alert(1)),url('javascript:alert(1)') - JSON/Script context:
</script><script>alert(1)//
- Attribute context:
- Double-Encoding: Testing
%253Cscript%253E(double URL-encoded) to bypass decode-then-validate logic - Template Injection: Testing for unsafe template rendering (
{{7*7}},${7*7},<%= 7*7 %>)
HTTP Evidence:
- Bypass payloads reflected unencoded in HTML responses (view-source shows
<scr<script>ipt>) - Successful attribute breakout visible in response HTML (
<img src="user-input" onload="...">) - JavaScript execution confirmed via HTTP callbacks to scanner infrastructure
- Incomplete encoding: quotes not encoded in attribute context,
<>encoded but quotes left unencoded - Double-encoding vulnerabilities:
%253Cbecomes%3Cafter first decode, then<after second - Context mismatches: HTML-encoded output placed in JavaScript context where HTML entities are invalid
Scanner Behavior: OWASP ZAP and PortSwigger Burp test context-specific bypasses systematically. They submit polyglot payloads that work across multiple contexts, analyze encoding applied to each character in the payload, and use automated browser testing to verify execution. Advanced scanners test both client-side (DOM-based) and server-side (reflected/stored) XSS with context-aware payloads.
Analyzing the Dynamic Scan Result
What the DAST Scanner Found
When reviewing your security scan results, you'll see:
HTTP Request Details
- URL and endpoint that triggered the finding
- HTTP method (GET, POST, etc.)
- Query parameters or form data with test payloads
- Request headers and body content
HTTP Response Evidence
- Response showing the vulnerability manifestation
- Evidence of improper handling or injection
- Runtime behavior indicators
Attack Vector
- Which parameter or input is vulnerable
- Type of exploitation possible
- Context where the vulnerability appears
Mapping DAST Findings to Source Code
Find the Vulnerable Endpoint
Use the HTTP request URL to locate the code:
# Search for escaping/encoding functions
grep -r "escape" src/
grep -r "sanitize" src/
grep -r "encode" src/
grep -r "strip_tags" src/
Locate Output Encoding
Common patterns to search for:
- Incomplete escaping: Only escaping
<>but not quotes - Wrong context: HTML encoding in JavaScript context
- Denylist filtering: Blocking specific tags instead of allowlisting
- Double encoding: Encoding twice causes bypass
- Client-side filtering: Can be bypassed
Find Insufficient Neutralization
Search for encoding issues:
# Find encoding attempts
grep -r "replace('<', '')" src/
grep -r "strip_tags" src/
grep -r "htmlspecialchars" src/
Trace to Vulnerable Operation
Look for improper neutralization:
- Wrong encoding: Using HTML encoding in JavaScript
- Incomplete encoding: Missing quotes, backslashes
- Denylist bypass: Filtering
<script>but allowing<img> - Double encoding:
%253Cscript%253Ebecomes<script> - Attribute injection: Not encoding quotes in attributes
Remediation
Core principle: Never rely on ad-hoc filtering or partial escaping for XSS; encode untrusted data for the specific output context (HTML, attribute, JS, URL) using vetted framework encoders so it cannot change interpretation.
Verification and Follow-Up Testing
After applying the fix:
Reproduce the Vulnerability
# Test filter bypasses
curl "http://localhost/search?q=<scr<script>ipt>alert(1)</scr</script>ipt>"
curl "http://localhost/msg?txt=<img src=x onerror=alert(1)>"
# Test context-specific bypasses
curl "http://localhost/user?name='; alert(1)//"
Verify the Fix
- Confirm context-appropriate encoding used
- Confirm contextual output encoding at the sink (HTML/attr/JS/URL)
- Confirm templates have auto-escaping enabled and unsafe “raw/safe” rendering is not used
- Confirm DOM sinks (innerHTML, document.write, etc.) are avoided or use safe APIs (textContent)
- Confirm CSP is defense-in-depth (not a fix)
- Ensure no double-encoding issues
- Test comprehensive HTML entity encoding
Test Edge Cases
# Bypassing denylist filters
<scr<script>ipt>alert(1)</scr</script>ipt>
<ScRiPt>alert(1)</sCrIpT>
<img src="x" onerror="alert(1)">
# JavaScript context
'-alert(1)-'
"; alert(1)//
# Attribute context
" onload="alert(1)
' onmouseover='alert(1)
# Double encoding
%253Cscript%253E
# Or use browser DevTools Network tab to copy as cURL
Re-run DAST Scanner
Run your dynamic scanner again on the fixed endpoint to confirm remediation.