Description
When JavaScript's replace searches for a string, as in $STR.replace('character', 'replacement'), it changes only the first occurrence. Using it to escape potentially dangerous characters can leave later occurrences unchanged, allowing an attacker to bypass the transformation.
Potential impact
- Script or SQL injection when output retains characters that are significant in its destination context
- Bypasses of incomplete escaping or filtering
Remediation
- If every occurrence must be replaced, use a regular expression with the global (
g) flag. Do not rely on quote replacement to prevent SQL injection; use parameterized queries for SQL. - Use an encoder appropriate to the output context. If HTML markup must be allowed, use a sanitization library such as DOMPurify.
- For HTML text, encode the required characters, including
&,<,>,", and', as HTML entities. Do not apply this treatment unchanged to JavaScript, CSS, URLs, or other contexts.
Examples
Before
javascript
function escapeQuotes(s) {
return s.replace("'", "''");
}
function escapeHtml(html) {
return html.replace("<", "<").replace(">", ">");
}
After
javascript
function escapeQuotes(s) {
return s.replace(/'/g, "''"); // Replace every single quote
}
function escapeHtml(html) {
return html.replace(/[&<>"']/g, (ch) => ({
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'",
}[ch])); // Encode the characters needed for HTML text
}
Explanation:
- Before:
.replace("character", ...)replaces only the first occurrence. Other characters required by the output context also need appropriate handling. - After: Regular expressions with the global (
g) flag process every occurrence. The HTML helper encodes the characters needed for HTML text, preventing bypasses caused by repeated characters.