Regex Injection
ID |
swift.regex_injection |
Severity |
critical |
Resource |
Injection |
Language |
Swift |
Tags |
CWE:1333, CWE:625, NIST.SP.800-53, OWASP:2004:A9, PCI-DSS:6.5.1 |
Description
Improper neutralization of external input used in a regular expression ('Regex Injection').
Regular Expression (Regex) Injection arises when input from users is directly concatenated into a regex pattern without validation or sanitization. This can allow an attacker to manipulate the pattern used for searching or matching, potentially bypassing validation or security checks.
Rationale
Consider the following example in Swift:
import Foundation
func validateInput(_ request: URLRequest) -> Bool {
guard let components = URLComponents(url: request.url!, resolvingAgainstBaseURL: false),
let userInput = components.queryItems?.first(where: { $0.name == "pattern" })?.value else {
return false
}
// VULNERABLE - Attacker can alter the regular expression,
// to bypass validations or to launch ReDoS attacks !
guard let regex = try? Regex(userInput) else { // injection
return false
}
let testString = "test@example.com"
return testString.contains(regex)
}
In this code, the userInput is directly used as a regex pattern. If the input contains special regex characters or sequences, it can alter the intended structure of the expression. For example, an input of ".*" could manipulate the pattern to match any string, circumventing the intended constraints.
The same vulnerability applies to NSRegularExpression:
func matchPattern(_ request: URLRequest, _ input: String) -> Bool {
guard let components = URLComponents(url: request.url!, resolvingAgainstBaseURL: false),
let pattern = components.queryItems?.first(where: { $0.name == "pattern" })?.value else {
return false
}
// VULNERABLE - User-controlled pattern
guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else { // injection
return false
}
let range = NSRange(input.startIndex..., in: input)
return regex.firstMatch(in: input, options: [], range: range) != nil
}
Remediation
To remediate Regular Expression Injection vulnerabilities, you can use the following strategies:
-
Escape User Input: Always escape user input to be included in regex patterns, using the library’s 'regexp quoting' functions.
-
Use Strict Validation: Implement strict validation on input prior to its inclusion in a regex pattern. Ensuring that input only contains expected characters significantly lowers the risk of injection.
-
Predefine Regular Expressions: Avoid dynamically building regex patterns based on user input. Instead, use predefined patterns that safely incorporate user input as part of the matching process.
-
Input Filtering: Apply input filtering to remove known harmful characters or sequences from user input before processing them in regex operations.
-
Security Tools and Static Analysis: Utilize SAST tools to identify regex injection vulnerabilities during the development process. These tools can provide early warnings about unsafe patterns or input concatenations.
By following these measures, you not only prevent Regular Expression Injection but also ensure that your application remains robust and secure against a variety of input-based exploits.
Swift provides Regex.escape() to safely handle meta-characters from user input, nullifying their special purpose. The vulnerable code above would become:
func validateInput(_ request: URLRequest) -> Bool {
guard let components = URLComponents(url: request.url!, resolvingAgainstBaseURL: false),
let userInput = components.queryItems?.first(where: { $0.name == "pattern" })?.value else {
return false
}
// FIX: neutralize input by escaping regex metacharacters
let escapedInput = Regex.escape(userInput)
guard let regex = try? Regex(escapedInput) else {
return false
}
let testString = "test@example.com"
return testString.contains(regex)
}
For NSRegularExpression, use NSRegularExpression.escapedPattern(for:):
func matchPattern(_ request: URLRequest, _ input: String) -> Bool {
guard let components = URLComponents(url: request.url!, resolvingAgainstBaseURL: false),
let userInput = components.queryItems?.first(where: { $0.name == "pattern" })?.value else {
return false
}
// FIX: Escape user input
let escapedPattern = NSRegularExpression.escapedPattern(for: userInput)
guard let regex = try? NSRegularExpression(pattern: escapedPattern, options: []) else {
return false
}
let range = NSRange(input.startIndex..., in: input)
return regex.firstMatch(in: input, options: [], range: range) != nil
}
Configuration
The detector has the following configurable parameters:
-
sources, that indicates the source kinds to check. -
neutralizations, that indicates the neutralization kinds to check.
Unless you need to change the default behavior, you typically do not need to configure this detector.
References
-
CWE-625 : Permissive Regular Expression.
-
CWE-1333 : Inefficient Regular Expression Complexity.
-
OWASP Top 10 2021 - A03 : Injection.