When we build Express, Koa, or other Node.js web apps, one behind-the-scenes library you’ll often find is path-to-regexp. It helps convert readable URL route definitions into lightning-fast regular expressions. But under the hood, not all regexes are created equal—and a recent vulnerability, CVE-2024-45296, shows how one false move can slam the brakes on your entire JavaScript server.

Let’s break down exactly what happened, how to exploit it, and—most important—how to keep your apps safe.

The Problem: Catastrophic Backtracking in JavaScript Regex

JavaScript handles pretty much everything—including regex matching—on its main thread. If a regex operation is slow, everything else in your app has to wait. This is called event loop blocking, and it leads straight to denial-of-service (DoS).

The path-to-regexp library helps route URLs by creating regexes from path patterns. But in some cases, a specific kind of route definition causes it to output a “evil regex”—one that causes backtracking so bad that it can freeze the app.

Here’s the dangerous case:
If you have two parameters within a single path segment, separated by something *other than* a period (.), path-to-regexp makes a regex vulnerable to runaway processing.

Example “bad” path to avoid:

'/user/:name-:id'

Here, both :name and :id are in the same segment, separated by a dash (-). This fires the bug.

Let’s see it in action! First, install a vulnerable version of path-to-regexp (say, .1.9)

npm install path-to-regexp@.1.9

Now, try this Node.js example

const pathToRegExp = require('path-to-regexp');
const route = '/user/:name-:id'; // Triggers the bad regex
const regexp = pathToRegExp(route);

const attackInput = '/user/' + 'A'.repeat(100) + '-' + 'B'.repeat(100);
const start = Date.now();
const match = regexp.exec(attackInput);
const end = Date.now();

console.log('Regex matching took:', (end - start), 'ms');

If the input is crafted just right, the process freezes or takes an unusually long time just to match! Imagine this happening live in your Express server, just because an attacker sends a long, malicious URL.

Why Does This Happen?

path-to-regexp tries to support flexible routes, but when two parameters live in one segment and are split by something besides a dot, the generated regex pattern can run into exponential backtracking thanks to the unbounded number of ways a string can be split between the params.

For example, /user/:foo-:bar translates to a pattern that, for certain long inputs, will make the regex engine check every possible way to divide the string between foo and bar. In regex, this can take ages as the input gets longer.

For users of version .1.x: Upgrade to at least .1.10, which fixes the vulnerable pattern.

- For everyone else: Upgrade to 8.. or later. This major version hardens the code and removes the dangerous behavior.

Latest versions are available via NPM

npm install path-to-regexp@latest

Or to get the last patch in the .1.10 train

npm install path-to-regexp@.1.10

Quick safety check:
If you use Express or another high-level framework, check its dependencies too (npm ls path-to-regexp). Sometimes you may need to update your framework to get a safe version downstream.

References and Further Reading

- Official GitHub Advisory for CVE-2024-45296
- NPM Advisory DB: path-to-regexp Denial of Service
- path-to-regexp Repository
- OWASP: Regular expression Denial of Service (ReDoS)

Conclusion

JavaScript servers have zero tolerance for slow regexes. Thanks to CVE-2024-45296, we now know that even trusted libraries like path-to-regexp can turn your event loop into a sitting duck if left unpatched. Audit your code, upgrade your dependencies, and watch out for odd-looking route definitions.

Safe coding! 🚦

*If this breakdown helps you, feel free to share with your team or drop a comment for more Node.js security deep-dives.*

Timeline

Published on: 09/09/2024 19:15:13 UTC
Last modified on: 09/10/2024 12:09:50 UTC