-
Notifications
You must be signed in to change notification settings - Fork 1.9k
JS/RB: write qhelp for incomplete-multi-character-sanitization
#13641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
QHelp previews: javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelpIncomplete multi-character sanitizationSanitizing untrusted input is a common technique for preventing injection attacks and other security vulnerabilities. Regular expressions are often used to perform this sanitization. However, when the regular expression matches multiple consecutive characters, replacing it just once can result in the unsafe text reappearing in the sanitized input. Attackers can exploit this issue by crafting inputs that, when sanitized with an ineffective regular expression, still contain malicious code or content. This can lead to code execution, data exposure, or other vulnerabilities. RecommendationTo prevent this issue, it is highly recommended to use a well-tested sanitization library whenever possible. These libraries are more likely to handle corner cases and ensure effective sanitization. If a library is not an option, you can consider alternative strategies to fix the issue. For example, applying the regular expression replacement repeatedly until no more replacements can be performed, or rewriting the regular expression to match single characters instead of the entire unsafe text. ExampleConsider the following JavaScript code that aims to remove all HTML comment start and end tags: str.replace(/<!--|--!?>/g, ""); Given the input string "<!<!--- comment --->>", the output will be "<!-- comment -->", which still contains an HTML comment. One possible fix for this issue is to apply the regular expression replacement repeatedly until no more replacements can be performed. This ensures that the unsafe text does not re-appear in the sanitized input, effectively removing all instances of the targeted pattern: function removeHtmlComments(input) {
let previous;
do {
previous = input;
input = input.replace(/<!--|--!?>/g, "");
} while (input !== previous);
return input;
} ExampleAnother example is the following regular expression intended to remove script tags: str.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/g, ""); If the input string is "<scrip<script>is removed</script>t>alert(123)</script>", the output will be "<script>alert(123)</script>", which still contains a script tag. A fix for this issue is to rewrite the regular expression to match single characters ("<" and ">") instead of the entire unsafe text. This simplifies the sanitization process and ensures that all potentially unsafe characters are removed: function removeAllHtmlTags(input) {
return input.replace(/<|>/g, "");
}Another potential fix is to use the popular const sanitizeHtml = require("sanitize-html");
function removeAllHtmlTags(input) {
return sanitizeHtml(input);
}ExampleLastly, consider a path sanitizer using the regular expression str.replace(/\.\.\//g, ""); The regular expression attempts to strip out all occurences of A possible fix for this issue is to use the "sanitize-filename" npm library for path sanitization. This library is specifically designed to handle path sanitization, and should handle all corner cases and ensure effective sanitization: const sanitize = require("sanitize-filename");
function sanitizePath(input) {
return sanitize(input);
} References
javascript/ql/src/Security/CWE-116/IncompleteSanitization.qhelpIncomplete string escaping or encodingSanitizing untrusted input is a common technique for preventing injection attacks such as SQL injection or cross-site scripting. Usually, this is done by escaping meta-characters such as quotes in a domain-specific way so that they are treated as normal characters. However, directly using the string In the former case, later meta-characters are left undisturbed and can be used to subvert the sanitization. In the latter case, preceding a meta-character with a backslash leads to the backslash being escaped, but the meta-character appearing un-escaped, which again makes the sanitization ineffective. Even if the escaped string is not used in a security-critical context, incomplete escaping may still have undesirable effects, such as badly rendered or confusing output. RecommendationUse a (well-tested) sanitization library if at all possible. These libraries are much more likely to handle corner cases correctly than a custom implementation. An even safer alternative is to design the application so that sanitization is not needed, for instance by using prepared statements for SQL queries. Otherwise, make sure to use a regular expression with the ExampleFor example, assume that we want to embed a user-controlled string function escapeQuotes(s) {
return s.replace("'", "''");
}As written, this sanitizer is ineffective: if the first argument to As mentioned above, the function If this is not an option, function escapeQuotes(s) {
return s.replace(/'/g, "''");
}Note that it is very important to include the global flag: References
ruby/ql/src/queries/security/cwe-116/IncompleteMultiCharacterSanitization.qhelpIncomplete multi-character sanitizationSanitizing untrusted input is a common technique for preventing injection attacks and other security vulnerabilities. Regular expressions are often used to perform this sanitization. However, when the regular expression matches multiple consecutive characters, replacing it just once can result in the unsafe text re-appearing in the sanitized input. Attackers can exploit this issue by crafting inputs that, when sanitized with an ineffective regular expression, still contain malicious code or content. This can lead to code execution, data exposure, or other vulnerabilities. RecommendationTo prevent this issue, it is highly recommended to use a well-tested sanitization library whenever possible. These libraries are more likely to handle corner cases and ensure effective sanitization. If a library is not an option, you can consider alternative strategies to fix the issue. For example, applying the regular expression replacement repeatedly until no more replacements can be performed, or rewriting the regular expression to match single characters instead of the entire unsafe text. ExampleConsider the following Ruby code that aims to remove all HTML comment start and end tags: str.gsub(/<!--|--!?>/, "") Given the input string "<!<!--- comment --->>", the output will be "<!-- comment -->", which still contains an HTML comment. One possible fix for this issue is to apply the regular expression replacement repeatedly until no more replacements can be performed. This ensures that the unsafe text does not re-appear in the sanitized input, effectively removing all instances of the targeted pattern: def remove_html_comments(input)
previous = nil
while input != previous
previous = input
input = input.gsub(/<!--|--!?>/, "")
end
input
end ExampleAnother example is the following regular expression intended to remove script tags: str.gsub(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/, "") If the input string is "<scrip<script>is removed</script>t>alert(123)</script>", the output will be "<script>alert(123)</script>", which still contains a script tag. A fix for this issue is to rewrite the regular expression to match single characters ("<" and ">") instead of the entire unsafe text. This simplifies the sanitization process and ensures that all potentially unsafe characters are removed: def remove_all_html_tags(input)
input.gsub(/<|>/, "")
end Another potential fix is to use the popular require 'sanitize'
def sanitize_html(input)
Sanitize.fragment(input)
endExampleLastly, consider a path sanitizer using the regular expression str.gsub(/\.\.\//, "") The regular expression attempts to strip out all occurences of A possible fix for this issue is to use the require 'facets'
def sanitize_path(input)
File.sanitize(input)
end References
ruby/ql/src/queries/security/cwe-116/IncompleteSanitization.qhelpIncomplete string escaping or encodingSanitizing untrusted input is a common technique for preventing injection attacks such as SQL injection or cross-site scripting. Usually, this is done by escaping meta-characters such as quotes in a domain-specific way so that they are treated as normal characters. However, directly using the In the former case, later meta-characters are left undisturbed and can be used to subvert the sanitization. In the latter case, preceding a meta-character with a backslash leads to the backslash being escaped, but the meta-character appearing un-escaped, which again makes the sanitization ineffective. Even if the escaped string is not used in a security-critical context, incomplete escaping may still have undesirable effects, such as badly rendered or confusing output. RecommendationUse a (well-tested) sanitization library if at all possible. These libraries are much more likely to handle corner cases correctly than a custom implementation. An even safer alternative is to design the application so that sanitization is not needed. Otherwise, make sure to use ExampleAs an example, assume that we want to embed a user-controlled string def escape_quotes(s)
s.sub "'", "''"
endAs written, this sanitizer is ineffective: As mentioned above, the method If this is not an option, def escape_quotes(s)
s.gsub "'", "''"
endReferences
|
a3540de to
6ac458b
Compare
incomplete-multi-character-sanitization
max-schaefer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice! A couple of initial comments.
|
|
||
| <references> | ||
| <li>OWASP Top 10: <a href="https://www.owasp.org/index.php/Top_10-2017_A1-Injection">A1 Injection</a>.</li> | ||
| <li>Stackoverflow: <a href="https://stackoverflow.com/questions/6659351/removing-all-script-tags-from-html-with-js-regular-expression">Removing all script tags from HTML with JS regular expression</a>.</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| <li>Stackoverflow: <a href="https://stackoverflow.com/questions/6659351/removing-all-script-tags-from-html-with-js-regular-expression">Removing all script tags from HTML with JS regular expression</a>.</li> | |
| <li>Stack Overflow: <a href="https://stackoverflow.com/questions/6659351/removing-all-script-tags-from-html-with-js-regular-expression">Removing all script tags from HTML with JS regular expression</a>.</li> |
| <p> | ||
| Sanitizing untrusted input is a common technique for preventing injection attacks and other security | ||
| vulnerabilities. Regular expressions are often used to perform this sanitization. However, when the | ||
| regex matches multiple consecutive characters, applying a regular expression replacement just once |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps better to consistently use "regular expression" everywhere instead of switching between that and "regex"?
| regex matches multiple consecutive characters, applying a regular expression replacement just once | |
| regular expression matches multiple consecutive characters, replacing it just once |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(There are a few more instances below.)
|
|
||
| <p> | ||
| If a library is not an option, you can consider alternative strategies to fix the issue. For example, | ||
| applying the regular expression replacement recursively until a fixpoint is reached or to rewrite the regular |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While talking about fixpoints is natural for us CodeQL types, it may not be sufficiently clear for everyone. Perhaps rephrase?
| applying the regular expression replacement recursively until a fixpoint is reached or to rewrite the regular | |
| applying the regular expression replacement repeatedly until no more replacements can be performed or to rewrite the regular |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(There are a few more instances further down.)
asgerf
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM apart from the two occurrences of "recursively" that seem like they should also be replaced with "repeatedly".
javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp
Outdated
Show resolved
Hide resolved
ruby/ql/src/queries/security/cwe-116/IncompleteMultiCharacterSanitization.qhelp
Outdated
Show resolved
Hide resolved
Co-authored-by: Asger F <asgerf@github.com>
max-schaefer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. I wonder whether it may be worth showing an example of using a library to deal with removal of HTML tags, we still seem to be heavy on hand-written regexes for this.
javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp
Outdated
Show resolved
Hide resolved
Co-authored-by: Max Schaefer <54907921+max-schaefer@users.noreply.github.com>
…anitization, and use the old text about ../ replacements
max-schaefer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM; just spotted another instance of the suggestion I made in the last round, which I forgot to point out.
ruby/ql/src/queries/security/cwe-116/IncompleteMultiCharacterSanitization.qhelp
Outdated
Show resolved
Hide resolved
Co-authored-by: Max Schaefer <54907921+max-schaefer@users.noreply.github.com>
javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.qhelp
Outdated
Show resolved
Hide resolved
Co-authored-by: Matt Pollard <mattpollard@users.noreply.github.com>
6631e83
The QHelp for
incomplete-multi-character-sanitizationwas just a direct clone of the QHelp forincomplete-sanitization.The QHelp did contain a paragraph about multi-char regexps, but generally the QHelp wasn't particularly helpful for developers that try to fix
incomplete-multi-character-sanitizationissues.So I wrote a separate QHelp for
incomplete-multi-character-sanitizationthat focuses on relevant fix-strategies for that query, which should be more helpful for anyone trying to fix issues from that query.I'm not sure about that last Ruby example, could a Ruby person confirm if that works? Or maybe suggest a better example?
GPT-4 helped me write the QHelp. As the initial prompt I gave it the
incomplete-sanitizationQHelp and my own description of someincomplete-multi-character-sanitizationexamples.The initial output from GPT-4 was surprisingly bad, but I got something I'm happy with after a lot of rounds of feedback and some manual adjustments.