-
Notifications
You must be signed in to change notification settings - Fork 6k
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
StrictHttpFirewall: Validate headers and parameters #8644
Conversation
fc09dc1
to
8a43119
Compare
cc3aba4
to
b29814e
Compare
@jzheaux do you have any feedback on this PR? I look forward to working with you to (hopefully) getting this merged. |
Thanks for adding these checks, @candrews, and sorry for the delay - I was just discussing this one internally with a teammate. I'm a little concerned about the implementation in that it has to check each character of each header name and value and each parameter name. That's a lot of data to churn through, especially considering that the implementation imposes no constraints. Thinking about Tomcat, a malicious user could send a 2MB request to the server and this class would have to parse through all of it. I ran a quick JMH benchmark on my machine to compare the existing StrictHttpFirewall to the proposed one and found that a malicious payload with the request body and headers maxed out changed the throughput for I wonder if changing the API to check a defined set of headers and parameters would be easier to manage. |
That's definitely not good - can you please provide the JMH benchmark you used so I can run it? I'd like to use it against some other implementation ideas and see how they do.
We could do that... but I'd prefer not to except as a last resort. |
Certainly, @candrews! I've changed the benchmark just now to have a larger max header size, which has changed some of the numbers. Here are my latest results:
https://github.com/jzheaux/spring-security-gh-8644 contains the code. |
@jzheaux I believe that the "Reject the NULL character in paths in StrictHttpFirewall" portion isn't impactful to performance, so I split that out into a separate PR: #8703 Hopefully that's non-controversial and can be merged. I'll continue to work on the headers and parameters changes in this pull request. |
I made some modifications to the test to check different approaches, here's my test project: https://github.com/candrews/spring-security-gh-8644 Some results:
Analysis:
Therefore, I think the regex approach is the way to go, and I've updated this PR accordingly. I agree that these numbers do look shocking, but I think it's important to keep in mind that this test is a microbenchmark. Any real world use case would not show a performance loss of close to this magnitude, as tasks such as processing data and do real work would dwarf the small amount of time added by this change. I believe this change does add value by making the firewall reject clearly invalid input that could cause (security) problems downstream, and that value outweighs its (in reality) small performance cost. |
Thanks for this deeper investigation, @candrews.
Agreed.
I think more testing is necessary to determine the impact a change like this will have on a whole application. While we want to be secure, it would be much less secure if the firewall were so slow that applications remove it altogether in order to overcome a performance bottleneck. In the meantime, an application can still achieve what you are advocating by creating a custom firewall class: public class CodepointFirewall implements HttpFirewall {
StrictHttpFirewall firewall = new StrictHttpFirewall();
@Override
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) {
// do codepoint checks
return this.firewall.getFirewalledRequest(request);
}
}
I wonder if inspecting the header and body are a different enough thing from inspecting the request line that they belong in a separate class. Or perhaps there's a way to change the contract to |
@candrews I was chatting with Rob this morning about this ticket, and he shared an idea that might resolve the performance concern, which would be to defer these checks until the value is read by the application. For example, the instance of What would you think of changing the PR to check headers and parameters lazily? |
That sounds like a good idea - I'll put doing so on my TODO list. Thank you for the feedback! |
I've done that. Parameter and header names and values are now lazily checked in Thanks again! |
9c566e5
to
3230879
Compare
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.
Thanks, @candrews, I think this looks a lot more doable. I've left some feedback inline.
web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java
Outdated
Show resolved
Hide resolved
web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java
Outdated
Show resolved
Hide resolved
web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java
Outdated
Show resolved
Hide resolved
web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java
Outdated
Show resolved
Hide resolved
c4a31ae
to
54c77ec
Compare
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.
Thanks for the updates, @candrews! I've left some additional feedback inline.
web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java
Show resolved
Hide resolved
web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java
Show resolved
Hide resolved
web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java
Outdated
Show resolved
Hide resolved
Awesome, @candrews, I'm glad we were able to find a path that satisfied both of us. In preparation for merging, will you please squash your commits and format the final commit message? |
Adds methods to configure validation of header names and values and parameter names and values: * setAllowedHeaderNames(Predicate) * setAllowedHeaderValues(Predicate) * setAllowedParameterNames(Predicate) * setAllowedParameterValues(Predicate) By default, header names, header values, and parameter names that contain ISO control characters or unassigned unicode characters are rejected. No parameter value validation is performed by default. Issue spring-projectsgh-8644
I am as well! It's always a pleasure working the Spring developers. Thank you for patience in this process and for your continued efforts.
If I had known that I'd be squashing to 1 commit, I wouldn't have carefully maintained 3 separate commits for each functional change 🤷 oh well - now I know better for next time! I've updated this PR with the squashed commit and compliant commit message. |
Ouch, @candrews! Sorry about that. :/ I'll try and communicate about commits better in the future. |
@candrews, thank you again! This is now merged into |
Improvements to StrictHttpFirewall: