| name | encoding-bypass-anti-pattern |
| description | Security anti-pattern for encoding bypass vulnerabilities (CWE-838). Use when generating or reviewing code that handles URL encoding, Unicode normalization, or character set conversions before security validation. Detects validation before normalization and double-encoding issues. |
Encoding Bypass Anti-Pattern
Severity: High
Summary
Encoding bypass evades security checks via alternate encodings. Occurs when validation happens before decoding/normalization. Encoded payload appears safe but becomes malicious after processing. Bypasses WAFs, input filters, enables XSS and SQL injection.
The Anti-Pattern
Flawed order of operations: Validate then Decode/Normalize. Security checks run on encoded data, application later uses decoded version, re-introducing the vulnerability.
BAD Code Example
import unicodedata
def is_safe_username(username):
if '<' in username or '>' in username:
return False
return True
def create_user_profile(username):
if not is_safe_username(username):
raise ValueError("Invalid characters in username.")
normalized_username = unicodedata.normalize('NFKC', username)
return f"<div>Welcome, {normalized_username}</div>"
GOOD Code Example
import unicodedata
def is_safe_username(username):
if '<' in username or '>' in username:
return False
return True
def create_user_profile(username):
normalized_username = unicodedata.normalize('NFKC', username)
if not is_safe_username(normalized_username):
raise ValueError("Invalid characters in username.")
return f"<div>Welcome, {normalized_username}</div>"
JavaScript/Node.js Examples
BAD:
const express = require('express');
const fs = require('fs');
const path = require('path');
app.get('/file/:filename', (req, res) => {
const filename = req.params.filename;
if (filename.includes('..')) {
return res.status(400).send('Invalid filename');
}
const filePath = path.join('/uploads', filename);
res.sendFile(filePath);
});
GOOD:
const express = require('express');
const fs = require('fs');
const path = require('path');
app.get('/file/:filename', (req, res) => {
let filename = decodeURIComponent(req.params.filename);
filename = path.normalize(filename);
if (filename.includes('..') || path.isAbsolute(filename)) {
return res.status(400).send('Invalid filename');
}
const filePath = path.join('/uploads', filename);
res.sendFile(filePath);
});
Java Examples
BAD:
import java.net.URLDecoder;
import java.sql.*;
public void searchUser(String encodedQuery) {
if (encodedQuery.contains("'") || encodedQuery.contains("--")) {
throw new SecurityException("Invalid characters");
}
String query = URLDecoder.decode(encodedQuery, "UTF-8");
String sql = "SELECT * FROM users WHERE name = '" + query + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
}
GOOD:
import java.net.URLDecoder;
import java.sql.*;
import java.util.regex.Pattern;
public void searchUser(String encodedQuery) {
String query = URLDecoder.decode(encodedQuery, "UTF-8");
if (!Pattern.matches("^[a-zA-Z0-9_]+$", query)) {
throw new SecurityException("Invalid characters");
}
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, query);
ResultSet rs = stmt.executeQuery();
}
Detection
Python:
- Validation before
unicodedata.normalize()
- Input checks before
urllib.parse.unquote()
- Regex patterns before string normalization
- HTML entity validation before
html.unescape()
JavaScript/Node.js:
- Validation before
decodeURIComponent()
- Path checks before
path.normalize()
- Express middleware order (validate before decode)
- Input checks before
Buffer.from(input, 'base64')
Java:
- Validation before
URLDecoder.decode()
- Security checks before
Normalizer.normalize()
- Input validation before
StringEscapeUtils.unescapeHtml()
- Path validation before
Paths.get().normalize()
PHP:
- Validation before
urldecode()
- Input checks before
html_entity_decode()
- Path validation before
realpath()
Search Patterns:
- Grep:
normalize\(|decode\(|unescape\(|URLDecoder|decodeURIComponent
- Look for validation logic (if statements, regex) before these functions
- Check for double decoding: multiple decode calls in sequence
- Review web framework routing (automatic decoding may occur)
Common Encoding Bypass Techniques:
- URL encoding:
%3c for <, %2e%2e%2f for ../
- Double URL encoding:
%253c for < (decoded twice)
- Unicode variants:
< (U+FF1C) for <
- HTML entities:
< or < for <
- Unicode escapes:
\u003c for <
- Mixed encoding:
%u003c or %c0%bc for <
- Path traversal:
..%2f, ..%5c, %2e%2e/
Prevention
Testing for Encoding Bypass
Manual Testing:
- Test URL encoding:
%3cscript%3e, ..%2f..%2f
- Test double encoding:
%253cscript%253e, ..%252f..%252f
- Test Unicode variants:
<script>, ../
- Test HTML entities:
<script>, <script>
- Test mixed encoding:
%u003cscript%u003e
- Verify filters catch all encoding variants
Automated Testing:
- Static Analysis: Semgrep, CodeQL to detect validation-before-decode patterns
- DAST: Burp Suite Intruder with encoding payloads, OWASP ZAP fuzzer
- Payload Lists: SecLists encoding bypass payloads
- Custom Scripts: Automated encoding variant generation
Example Test:
def test_encoding_bypass_prevention():
malicious_input = "<script>alert(1)</script>"
try:
create_user_profile(malicious_input)
assert False, "Should reject encoded malicious input"
except ValueError:
pass
encoded_input = "%253cscript%253e"
try:
search_user(encoded_input)
assert False, "Should reject double-encoded input"
except SecurityException:
pass
Burp Suite Test:
# Intruder payload positions
GET /file/§..%2f..%2fetc%2fpasswd§
# Payload list (encoding variants)
..%2f..%2f
..%252f..%252f
../../
%2e%2e%2f%2e%2e%2f
Remediation Steps
- Identify decoding operations - Use detection patterns to find decode/normalize functions
- Trace data flow - Follow user input from entry to security validation
- Check validation order - Verify decode/normalize happens before validation
- Reverse order if needed - Move normalization before security checks
- Add missing normalization - Insert decode/normalize if absent
- Test with encoding variants - Use payload list from Testing section
- Verify canonicalization - Ensure all paths/URLs are normalized
- Review framework behavior - Check for automatic decoding in web framework
Related Security Patterns & Anti-Patterns
References