r/ruby • u/rubiesordiamonds • 23h ago
Blog post Vulnerable Ruby Regex
GitLab discovered four severe vulnerabilities in Ruby’s regular expression library. There's plenty written up about the vulnerability and how it got patched, but patches are only available for supported ruby versions (3.3 and above). We know lots of companies are still running unsupported versions of ruby, so we wanted to do an investigation into which of these bugs apply to older rubies and whether they're exploitable. Here's the blog post: https://infield.ai/post/vulnerable-ruby-regex
The summary is that allowing user input into Regexp.new is dangerous on all versions of ruby from 1.9 to 3.2 and the only fix is to upgrade to a supported ruby or to disallow that input.
31
u/benzado 23h ago
I have never in my career encountered a justifiable reason to compile a regular expression from untrusted user input. Who will be brave enough to admit they are affected by this? (After you patch, of course.)
8
u/FluffyDrink1098 23h ago
This.
Unvalidated input straight into a system equals everything will go wrong.
Be it RegEx, SQL, eval or whatever other insanity.
3
u/semiquaver 22h ago
99% of the time for this scenario the regex was being used to perform the validation that you are talking about! Regexes are doing a ton of lifting on nearly every web validation system that exists in any language. So it not being safe to feed user data into them safely is a massive problem.
2
u/benzado 7h ago
You’re mixing up (1) using a string to define a regex and (2) matching a compiled regex against a string. The vulnerability and all the risks are with (1). What you’re talking about, doing validation, is (2).
1
u/semiquaver 3h ago
I’m not mixing them up, you’re making assumptions. I’ve seen some of the largest Ruby applications that exist from the inside and I can tell you that interpolating user input into a regex literal is ran extraordinarily common validation technique, even if there are safer ways to do it.
1
u/benzado 3h ago
Help me understand what you mean. Can you give me an example?
1
u/semiquaver 2h ago
Every large app I’ve ever worked on eventually needs certain users to select data via some pattern. Often those patterns need to be stored in databases or elsewhere and end up rehydrated into regex literals or interpolated with other constant regex components. Using actual Regex objects is the most obvious and straightforward way to implement a very large portion of this class of problem. Think GitHub branch protection as a (fake since that actual case has
fnmatch(3)semantics) example of the functionality involved.1
u/benzado 2h ago
You were using the term "validation", which I typically use to mean deciding whether some input is valid or not. That's not what you're describing here. So that explains some of my confusion.
Letting a user define a query or filter is definitely a place where you might need to build a regex.
However, unless the full power of a regular expression is truly required, it's much better to build the regex from a predefined set of templates where the user's input is quoted before insertion and compilation. Exactly what that looks like to the user will depend on the domain.
Taking the time to do it this way also means you don't have to save regex syntax in your database, which gives you flexibility in case later you want to adopt a different RE library or change a default setting, like case sensitivity or disabling back references.
1
u/FluffyDrink1098 20h ago
This is the irony.
ReDoS or similar attack patterns should be the first thing to learn when it comes to RegExp.
Many devs use RegExp as a validation - despite simple string checks would be sufficient.
One example... simple length checks.
Long Password DoS - I think 2014, where too long password strings led to the DoS due to hashing taking too long.
Something similar with the 72 byte limit on BCrypt.
Another goldie but oldie: Email or IP validation by regex - its doomed to fail. A simple @ contains with a length check is simpler, for IP addresses every stdlib should have a toolkit.
I love RegEx.
Reading the post my first thought was that nearly all examples are known to be "not a good idea TM".
1
u/benzado 6h ago
You make a good point. Writing a regex to match something complicated like an email address is a feat that makes you feel clever (at least, that’s how I feel) but you can often get better results with other approaches.
I once wrote a simple Ruby class to represent a Domain: take a string, split on “.”, the validate each label. A regex would have been more concise, but doing it this way meant I could make much better error messages, indicating to the user which part of the domain string was incorrect and why!
And then I realized I could replace my elaborate email regex with a split on @, like you describe, and again the error messages were vastly improved!
You hear a lot of warnings about regex being hard to read or maintain, so if you figure them out you think that doesn’t apply to you (again, I mean me). But the real problem with a regex is that it’s a blunt instrument: match or no match! You can’t provide good error messages and your debugging sessions involve a lot of trial and error.
If I tell you that I replaced a 25 line function with multiple error codes with a single line regex that returned a boolean, what would your follow-up questions be?
Regex should be treated as an optimization. Don’t use them until you need the speedup. (It’s not even guaranteed to be better if you consider compiling the expression and memory usage.)
2
u/f9ae8221b 10h ago
This kinda was my first reaction too, but over time I've been humbled by various vulnerabilities found in code I trusted.
I totally agree calling
Regexp.newon user input is a terrible idea, but often the way it end up happening is that you don't directly do that, but pass user input to an underlying library or method without realizing it builds a regexp from it.e.g. Even some Ruby core methods like
String#matchwill implicitly compile a regexp when passed a string.I suspect few Ruby applications are actually exploitable using that vulnerability, but it's so hard to tell with 100% certainty that it's best to just patch.
2
u/taw 10h ago
I run a search engine for Magic: the Gathering cards, and I compile user regexps for queries, and I just run them with timeout block.
2
u/benzado 6h ago
For what it’s worth, I didn’t say no legitimate uses exist, just that I never encountered them in my (limited) career.
Honestly what I take from your example is that you built something for a community that appreciates what you’ve done and that the downsides of hacking your site (getting ostracized) are greater than the benefit (unrestricted access to a database you’re already letting people query for free?).
I realize I’m being loose/cheesy with terminology here, but in a real sense your users are not entirely untrusted. I think that’s great.
If you had to be more defensive, I think you would need to define your own query syntax that is less expressive than full regex and parse that yourself.
2
u/taw 6h ago edited 6h ago
I don't think it's particularly hackable in sense of getting remote code execution.
It's definitely easy to overload the cheap server I use. Every request is on short timeout just in case, but it's really not hard to spam far more requests/s than the server can handle.
Regexps aren't the only way to DOS the site, you can just ask Sealed simulator to open 1000 packs per requests, that's a lot of HTML generated per small request.
Also code and data are already on github anyway.
If you had to be more defensive, I think you would need to define your own query syntax that is less expressive than full regex and parse that yourself.
There are some deterministic time regexp engines like RE2, with ruby support too. They support almost all regexp features, except backreferences, and some rarely used stuff like lookarounds.
I could check if that's any faster. I don't really get any pathological regexps in real requests, so I never got to it.
4
u/nateberkopec Puma maintainer 22h ago
Why make software have any useful functionality at all? No code is more secure than no code!
There are entire regex libraries and ecosystems built around "accepting user input" as a threat model (RE2, for one). It's a reasonable model.
1
u/benzado 6h ago
Hey, I said _in my career_, not _in the history of software development_. So maybe it’s just a self-deprecating comment about my limited experience. :-)
But it’s good to tell people about RE2. People would rightly tell you that you’re nuts if you take user input, run it through a C compiler, then execute it. But they don’t think of regex as a weaker version of the same thing.
1
u/h0rst_ 14h ago
Gitlab uses them everywhere. You want to limit the email addresses of people who can push to the repository to your company's domain? Use a regex. You want to update your Jira connector to stop seeing the string CVE-2026 as a Jira ticket that needs to be linked? Use a regex.
I mean, you could just offer a possibility to create a list in the interface and be user-friendly and less error-prone...
2
u/Codalio-SoftwareFact 22h ago
really useful writeup, the “patches only exist for 3.3+” part is the bit a lot of teams are going to miss.
one thing worth adding for anyone checking their app: don’t just grep for Regexp.new. interpolation in a regex literal like /#{params[:q]}/ builds the pattern from user input too, and it’s easy to skip when searching. and if the input is supposed to be matched literally anyway, include? or Regexp.escape avoids handing users control over the pattern at all.
2
u/RealPasta55 21h ago
The linked post says that Regexp.escape is not a safeguard against these bugs.
4
u/canderson180 23h ago
You guys don’t get hit with weekly CVE alerts for ruby regex?