Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AppSec crashes when parsing integer HTTP headers (undefined method `tr' for nil:NilClass (NoMethodError)) #3782

Closed
TomNaessens opened this issue Jul 15, 2024 · 5 comments
Assignees
Milestone

Comments

@TomNaessens
Copy link

TomNaessens commented Jul 15, 2024

Hi all, 👋

We often have bots hit our systems with malformed HTTP headers. One of the malformed HTTP headers they apparently try (a lot), is a plain number (e.g. HTTP_2). This causes the following line to throw an "undefined method `tr' for nil:NilClass (NoMethodError)":

h[k.gsub(/^HTTP_/, '').downcase!.tr('_', '-')] = v if k =~ /^HTTP_/

k =~ /^HTTP_/ is matched, k.gsub(/^HTTP_/, '') results into "2", which becomes nil through downcase!:

[527]dev(main):0> "2".downcase!
=> nil

I'm not exactly sure what the right solution is, given that we're already working with a new object after the gsub, a regular downcase (without !) might work (although that will create another object):

[526]dev(main):0> "2".downcase
=> "2"

To reproduce, spin up a Rails app with dd trace, enable appsec, instrument Rack, and send a request with an integer as HTTP header:

 ~  curl http://localhost:3001 -H "2: 3"
Puma caught this error: undefined method `tr' for nil:NilClass (NoMethodError)
@TomNaessens TomNaessens changed the title AppSec crashes when parsing malformed HTTP headers (undefined method `tr' for nil:NilClass (NoMethodError)) AppSec crashes when parsing integer HTTP headers (undefined method `tr' for nil:NilClass (NoMethodError)) Jul 15, 2024
@lloeki
Copy link
Member

lloeki commented Jul 15, 2024

Good catch! Thanks for the report.

I'm not exactly sure what the right solution is

Probably something like:

h[k.gsub(/^HTTP_/, '').tap(&:downcase!).tr('_', '-')] = v if k =~ /^HTTP_/ 

And to save another allocation:

h[k.gsub(/^HTTP_/, '').tap(&:downcase!).tap { |s| s.tr!('_', '-') }] = v if k =~ /^HTTP_/ 

Note: not using gsub! because we don't want to mutate the original key.

@TomNaessens
Copy link
Author

I'm wanting to open a PR and write some tests for this, one last question: what's the expected behaviour when such a header is sent? With the .taps, "malformed" headers not containing text will end up in the hash as nil => value. Is that intended, or should they just not end up in the HeaderCollection at all?

@vpellan
Copy link
Contributor

vpellan commented Jul 24, 2024

Hello ! We have pushed a fix that should be included in the next release ! As for the headers that does not contain any text, they should be filtered out by Rack, but if it does happen, we still do want to send them to the WAF.

@vpellan vpellan self-assigned this Jul 24, 2024
@vpellan vpellan closed this as completed Jul 24, 2024
@ivoanjo ivoanjo added this to the 2.3.0 milestone Jul 24, 2024
@TonyCTHsu
Copy link
Contributor

👋 @TomNaessens We've released a 2.3.0, give it a try!

@TomNaessens
Copy link
Author

Awesome, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants