Level Up Coding

Coding tutorials and news. The developer homepage gitconnected.com && skilled.dev && levelup.dev

Follow publication

ReDoS Attacks: How Regex Can Bring Down Your System and How to Avoid Them

Garbel N.
Level Up Coding
Published in
6 min readSep 5, 2023

--

Image composition by the author using DALL-E

ReDoS

Regex and Backtracking

Examples

var sw = new Stopwatch();

for (int i = 10; i <= 25; i++)
{
var r = new Regex($@"^(\w\d|\d\w){{{i}}}$");
string input = new('1', (i * 2) + 1);

sw.Restart();
r.IsMatch(input);
sw.Stop();
Console.WriteLine($"{i}: {sw.Elapsed.TotalMilliseconds:N}ms");
}
10: 0.79ms
11: 0.52ms
12: 1.35ms
13: 2.38ms
14: 4.75ms
15: 8.80ms
16: 9.50ms
17: 16.63ms
18: 39.56ms
19: 72.23ms
20: 144.57ms
21: 372.80ms
22: 1,267.67ms
23: 2,961.73ms
24: 6,391.17ms
25: 13,355.15ms
The execution time vs the input length of the exponential regex pattern matching
var pattern = @"^[0-9A-Z]([-.\w]*[0-9A-Z])*$";
var input = "AAAAAAAAAAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!@contoso.com"; // nearly matched, the character "!" make it to not match.
var isMatched = Regex.IsMatch(input, pattern2, RegexOptions.IgnoreCase);
The CPU usage with 5 sessions
package main

import (
"fmt"
"regexp"
"strings"
"time"
)

func main() {
for i := 10; i <= 25; i++ {
re := regexp.MustCompile(fmt.Sprintf(`^(\w\d|\d\w){%d}$`, i))
input := strings.Repeat("1", (i*2)+1)

startTime := time.Now()
re.MatchString(input)
elapsed := time.Since(startTime)
fmt.Printf("%d: %.6fms\n", i, float64(elapsed.Milliseconds()))
}
}

How to guess the evil regex patterns?

Screenshot: example of a vulnerable regex pattern using Devina.
Screenshot: example of a safe regex pattern using Devina.

How to avoid?

try
{
var input = "some random text again and again and again.";
var pattern = "^([a-zA-Z0-9]+\\s?)*$";
var matchTimeout = TimeSpan.FromMilliseconds(1500);

var isMatched = Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase, matchTimeout);

Console.WriteLine(isMatched);
}
catch (RegexMatchTimeoutException rex)
{
// do something with the exception
}
  var input = "some random text again and again and again.";
var pattern = "^([a-zA-Z0-9]+\\s?)*$";
var matchTimeout = TimeSpan.FromMilliseconds(1500);

var isMatched = Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.NonBacktracking, matchTimeout);

Console.WriteLine(isMatched);
var sw = new Stopwatch();

for (int i = 10; i <= 25; i++)
{
var r = new Regex($@"^(\w\d|\d\w){{{i}}}$", RegexOptions.NonBacktracking);
string input = new('1', (i * 2) + 1);

sw.Restart();
r.IsMatch(input);
sw.Stop();
Console.WriteLine($"{i}: {sw.Elapsed.TotalMilliseconds:N}ms");
}
10: 0.14ms
11: 0.14ms
12: 0.12ms
13: 0.10ms
14: 0.11ms
15: 0.11ms
16: 0.12ms
17: 0.12ms
18: 0.12ms
19: 0.13ms
20: 0.14ms
21: 0.15ms
22: 0.16ms
23: 0.16ms
24: 0.16ms
25: 0.17ms

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Written by Garbel N.

Believes that science and technology can and should make the world a better place

No responses yet

Write a response