-
Notifications
You must be signed in to change notification settings - Fork 5k
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
Add ParameterLimitValve to enforce request parameter limits for specific URLs #753
base: main
Are you sure you want to change the base?
Conversation
- Introduced ParameterLimitValve to allow limiting the number of parameters in HTTP requests. - Added configuration options for global parameter limits and specific limits for URL patterns. - Requests exceeding the configured limits are rejected with an HTTP 400 Bad Request error. - Includes unit tests (TestParameterLimitValve) to ensure full test coverage for the valve's functionality. - Added documentation for the ParameterLimitValve, detailing its attributes, usage, and configuration.
This approach could be implemented as a Filter. If we were going to do this, I think something that enforces the limit at the point the parameters are parsed - rather than after - is the way to go. It would mean some refactoring of |
While I haven't actually looked at the code (for either Allowing a Valve/Filter to recover from this means that that DoS protection is effectively gone, even if the request is ultimately rejected with a 4xx response. The damage has already been done. |
I will refactor the implementation as follows:
Please let me know if this sounds like a reasonable solution, or if there’s anything else to consider that I am missing. |
Hash collisions was why the 10k limit was put in place - CVE-2012-0022. That was chosen as the largest round number that was low enough to avoid the hash collision issue. The further reduction to 1k was on the basis that very few apps need the limit that high and there is a memory cost to handling parameters. The aim was to reduce the minimum amount of RAM Tomcat needed to have and still be able to handle default maximum concurrent requests with default maximum parameters each. Like all the Tomcat defaults, it is a trade-off. |
…t the point when parameters are parsed - rather than after. - Introduced a new maxParameterCount field in Request.java class - Removed configuration option for global parameter limit, as it is replaced by the default Connector's limit. - Replaced unit tests with integration tests to ensure better coverage for the valve's functionality. - Fixed documentation
I have refactored the valve and the request as discussed here. |
mockRequest.setHeader(valve.getSslCipherUserKeySizeHeader(), "not-an-integer"); | ||
|
||
try { | ||
valve.invoke(mockRequest, null); | ||
} catch (NumberFormatException e) { | ||
Assert.assertNull(mockRequest.getAttribute(Globals.KEY_SIZE_ATTR)); | ||
mockRequest.setHeader(valve.getSslCipherUserKeySizeHeader(), null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to the random ordering of the execution of the tests, I had to reset the header at this point because other tests were throwing NumberFormatException.
This is an effort of introducing Parameter Limit Valve to allow limiting the number of parameters in HTTP requests, but explicitly allowing more parameters for specific URLs. (The idea raised by this email)
It's worth to be noted that if the Parameter Limit Valve is configured, it operates independently of the Connector's maxParameterCount attribute. The Connector's maxParameterCount sets a global limit, while the Parameter Limit Valve offers additional flexibility by allowing different limits for specific URLs. However, if the maxParameterCount defined in the Connector is lower, it effectively overrides the valve by preventing large requests from ever reaching it.
For manual testing one can add something like the following in context.xml
and run some relevant test cases:
curl -X POST http://localhost:8080/api/resource -d "param1=val1¶m2=val2" PASS
curl -X POST http://localhost:8080/api/resource -d "param1=val1¶m2=val2¶m3=val3" FAIL
curl -X POST http://localhost:8080/admin/settings -d "param1=val1" PASS
curl -X POST http://localhost:8080/admin/settings -d "param1=val1¶m2=val2" FAIL
curl -X POST http://localhost:8080/my/special/url1 -d "param1=val1¶m2=val2¶m3=val3" PASS
curl -X POST http://localhost:8080/my/special/url1 -d "param1=val1¶m2=val2¶m3=val3¶m4=val4" FAIL
curl -X POST http://localhost:8080/random -d "param1=val1¶m2=val2¶m3=val3¶m4=val4" PASS
curl -X POST http://localhost:8080/random -d "param1=val1¶m2=val2¶m3=val3¶m4=val4¶m5=val5" FAIL