Description
Building regular expressions directly from user-supplied patterns can let attackers choose expensive or inefficient expressions. Processing them may consume resources for a long time and disrupt the service.
Potential impact
- Denial of service when an inefficient pattern slows or stalls the server
- Poor responsiveness and instability for legitimate users
Remediation
- Define patterns whose execution cost has been reviewed in code. Do not use user input directly as a pattern.
- If dynamic selection is needed, choose from an allow-list of reviewed patterns and limit the length of the text being tested.
- Use analysis tools such as recheck to support review. Defining a pattern as a constant does not by itself bound its execution cost.
Examples
Before
javascript
// Before
function search(text, pattern) {
// Use user input directly as the regular expression pattern.
const regex = new RegExp(pattern);
return regex.test(text);
}
// Example usage
search("username", userInputPattern);
After
javascript
// After
function search(text) {
// Use a fixed regular expression.
const regex = /^\w+$/;
return regex.test(text);
}
// Alternatively, restrict dynamic pattern selection.
function searchSafe(text, pattern) {
// Use only allowed patterns from a predefined list.
const allowedPatterns = ["^\\w+$", "^\\d{4,8}$"];
if (!allowedPatterns.includes(pattern)) {
throw new Error("허용되지 않은 패턴입니다.");
}
const regex = new RegExp(pattern);
return regex.test(text);
}
Explanation:
- Before: User input directly controls the pattern. Complex patterns may take excessive time and expose the service to denial of service.
- After: A reviewed fixed pattern or allow-list prevents attackers from supplying their own expensive patterns.