Skip to content

Regular Expression Injection

What does this mean ?

Regex injection attacks should be avoided while working with untrusted data. Regex injection can be used to maliciously modify a regular expression, cause it to match unintended results, or cause it to consume too much CPU, resulting in a Denial of Service attack.

What can happen ?

When processing an input, most regular expression engines employ backtracking to examine all potential execution pathways of the regular expression. This can produce performance concerns, which are known as catastrophic backtracking scenarios. In the worst-case scenario, the regular expression's complexity grows exponentially with the amount of the input, which implies even a modest carefully-crafted input (such as 20 characters) might cause catastrophic backtracking and application denial of service.

Recommendation

  • When utilizing regular expressions, always employ a match timeout.
  • Regular expressions based on user input should be avoided at all costs.
  • Call (System.Text.RegularExpressions.Regex.Escape) or similar method to escape special characters from user input.
  • Allow only non-special characters to be entered by the user.

Sample Code

Vulnerable :

using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc;

namespace WebApplicationDotNetCore.Controllers
{
    public class RSPEC2631RegExpInjectionNoncompliantController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Validate(string regex, string input)
        {
            bool match = Regex.IsMatch(input, regex); // Noncompliant

            return Content("Valid? " + match);
        }
    }
}

Non Vulnerable :

using System;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc;

namespace WebApplicationDotNetCore.Controllers
{
    public class RSPEC2631RegExpInjectionCompliantController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Validate(string regex, string input)
        {
            bool match = Regex.IsMatch(input, Regex.Escape(regex)); // Compliant

            return Content("Valid? " + match);
        }
    }
}

Vulnerable :

public boolean validate(javax.servlet.http.HttpServletRequest request) {
  String regex = request.getParameter("regex");
  String input = request.getParameter("input");

  input.matches(regex);  // Noncompliant
}

Non Vulnerable :

public boolean validate(javax.servlet.http.HttpServletRequest request) {
  String regex = request.getParameter("regex");
  String input = request.getParameter("input");

  input.matches(Pattern.quote(regex));  // Compliant : with Pattern.quote metacharacters or escape sequences will be given no special meaning
}

Vulnerable :

$regex = $_GET["regex"];
$input = $_GET["input"];

preg_grep($regex, $input); // Noncompliant

Non Vulnerable :

$regex = $_GET["regex"];
$input = $_GET["input"];

preg_grep(preg_quote($regex), $input); // Compliant

Non Vulnerable :

const safeRegex = require('safe-regex');
const emailRegex = /^([a-zA-Z0-9])(([\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/;

// should output false because the emailRegex is vulnerable
console.log(safeRegex(emailRegex));

Vulnerable :

(req, res) => {
    const pattern = RegExp(req.query.pattern); // Vulnerable
    pattern.test(req.query.input);
};

Non Vulnerable :

const escapeStringRegexp = require('escape-string-regexp');

(req, res) => {
    const pattern = RegExp(escapeStringRegexp(req.query.pattern)); // Non Vulnerable
    pattern.test(req.query.input);
};

Vulnerable :

class HomeController < ActionController::Base
    def example_one
        # Vulnerable
        regex = /#{ params[:key] }/
    end

    def example_two
        # Vulnerable
        regex = Regexp.new(params[:key])
    end
end

Non Vulnerable :

class HomeController < ActionController::Base
    def home
        # Non Vulnerable
        regex = Regexp.new(Regex.escape(params[:key]))
    end
end

References