-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Raised Invalid request parameters
errors using application/x-www-form-urlencoded
as content type
#2326
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
Comments
Yes, are you expecting more than 4000 parameters? |
Thank you for your answer. Yes. I thought |
If you are using more than 4096 parameters, you need to configure a higher limit. the easiest way to do so is by setting the I apologize that this negatively affected your application, but in order to avoid denial of service, we had to pick a limit, and your application is apparently over the limit. |
We updated today and started getting these errors as well. We didn't expect a change like this to come in a patch version bump. I can sort of understand where this is coming from, but I feel like this is something that would usually happen at a higher proxy level, no? We have configured an explicit |
Unfortunately, there isn't a way to fix the denial of service issue other than placing a limit on the number of parameters. On 64-bit platforms (all modern platforms really) Ruby objects are 40 bytes each. You need one for the key and one for the value, so that is 80 bytes per parameter. The parameter value can be empty. With 4 characters per parameter (3 for key and 1 for the As mentioned above, we had to pick a limit, and your application is apparently over the limit. I apologize that this negatively affected your application, but in order to avoid denial of service, this change was necessary. You should use |
Forget to account for the |
My two previous comments implied that you could actually cause that much memory usage after the patch, but that isn't accurate. The reason we added the limit on the number of parameters is to prevent that much memory usage. Please take my 2 previous comments as what would be possible with just a bytesize limit and no limit on the number of parameters. |
Thanks a lot for the explanations @jeremyevans, makes a lot more sense now, I can see how this can be used by malicious actors. We'll give it some thought and see what we can do! |
For our app, I noticed that adding this piece of middleware to the start of our chain fixed the failure. Our endpoint which was failing was a SOAP endpoint, a POST request that included a 500kb payload in the body. I don't know rack well enough to know if this is going to cause issues elsewhere. class SetContentTypeFirst
def initialize(app)
@app = app
end
def call(env)
if env['CONTENT_TYPE'].nil? && env['HTTP_CONTENT_TYPE'].present?
env['CONTENT_TYPE'] = env['HTTP_CONTENT_TYPE']
end
@app.call(env)
end
end |
If your server/middleware is putting |
Thanks for the quick response! I'll have to figure out what's going on on my end. |
This broke our CI suite as well, even though we are fronted by nginx to handle max body size. I'm having issues with getting it to play nicely with dotenv however, and I'm not sure if the problem is me or not. But setting |
If you would like to set the limits inside the app: Rack::Utils.default_query_parser = Rack::QueryParser.make_default(32,
bytesize_limit: 4194304, params_limit: 4096) |
This also seems to break mailgun email ingestion with attachments over 4MB. |
That worked, thanks @jeremyevans. One thing I realized when setting that up is that we are actually on rack 2.x (because of rails 6). So its possible that was contributing to my initial issue. Also, for anyone else on rack 2, the
|
Then mailgun needs to increase the bytesize limit (shown earlier in thread), assuming they want to accept larger attachments. |
Sorry no by that I meant when Mailgun delivers the emails to our Rails apps and the attachment is over 4MB then it's now being rejected by this Rack change, so everyone using Mailgun to receive emails of that size (and presumably likewise with other similar providers) is going to have to make this change. |
I think the changelog should've noted this. Seems a lot of people have had it break their applications. Fortunately we caught it in test. |
Should we consider increasing the default bytesize limit? e.g. maybe 10MiB? |
I think the main thing is that many people are already protected by eg nginx, and so its a bit annoying to "double configure" the settings here. I solved our usecase above, but might have appreciated if there was just a "disable" option. |
I kind of agree with @J-Swift, this feels like a setting that we can disable. Maybe it should even default to off and only be enabled by users that are using Rack directly and not behind nginx. |
Following on from setting a limit inside the app, would the following middleware that changes these limits based on paths configured be a good idea? e.g. # lib/middleware/custom_path_query_parser.rb
#
class CustomPathQueryParser
def initialize(app, limited_paths = {})
@app = app
@limited_paths = limited_paths
end
def call(env)
path = env['PATH_INFO']
limit = @limited_paths[path]
Rack::Utils.default_query_parser = Rack::QueryParser.make_default(Rack::Utils.param_depth_limit)
return @app.call(env) unless limit
Rack::Utils.default_query_parser = Rack::QueryParser.make_default(
Rack::Utils.param_depth_limit,
bytesize_limit: limit
)
@app.call(env)
end
end # config/application.rb
#
require_relative '../lib/middleware/custom_path_query_parser'
module Appy
class Application < Rails::Application
...
# No idea where to place it at the moment 🤷♂️
config.middleware.insert_before 1, CustomPathQueryParser, {
'/rails/action_mailbox/mailer/inbound_emails' => 104_857_600 # ~100MB
} Ideally we only want to increase this limit for a specific paths, and not globally. The above code would allow us to set a custom limit for the |
The middleware isn't thread safe. It's also inefficient (it would be faster to create appropriate query parsers up front, instead of creating one per request). I assume a better approach would be to override the equivalent of Rack::Request#query_parser for the action mailbox controller to use a query parser with a higher limit, assuming it is calling that method and not Rack::Utils.default_query_parser directly. |
ISTR I've proposed this before but I can't find the exact reference. Anyway, I don't think it would be a bad idea to provide local per-request overrides in class CustomPathQueryParser
def initialize(app, query_parsers = {})
@app = app
@query_parsers = query_parsers
end
def call(env)
path = env['PATH_INFO']
env["rack.query_parser"] = @query_parsers[path]
@app.call(env)
end
end Ideally we do this consistently for all similar globals, IOW, rack request handling can be fully defined by the contents of |
These limits are being triggered for us in unexpected ways. Users attempting to upload files have hit limits on the number of parameters when attempting to upload a file. This was traced back to the fact that rack treats POST requests with a missing Content-Type header as form data and thus eligible for parameter parsing (test case). In this scenario it really doesn't make sense for us to increase the number of parameters since the body is not intended to be parsed. We'll probably resort to setting a default Content-Type header on appropriate paths. |
This should only be a problem if code somewhere is asking rack to parse body params for the request (calling
Might be useful to override Rack::Request#POST to print the call stack, then submit the request without content-type, and see what code path is hitting it. |
We updated
rack
tov2.2.14
to correspond a security issue of CVE-2025-46727. And then we occuredActionController::BadRequest: Invalid request parameters: total number of query parameters (xxxx) exceeds limit (4096)
errors.Our service uses
application/x-www-form-urlencoded
as a content type in POST requests. According to test cases inrack/test/spec_request.rb
, I think thatrack
will parse a request body ofapplication/x-www-form-urlencoded
asparams
. Sorack
raises that error by limiting a length of params is over.Is this behavior intentional?
The text was updated successfully, but these errors were encountered: