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
Reject HTTP/2 header values with invalid characters #12760
Conversation
@ejona86 PTAL as well |
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
...c-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testStaticTableEntries.json
Outdated
Show resolved
Hide resolved
@normanmaurer Comments addressed. |
a0f5d3d
to
b3d925c
Compare
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
@@ -416,6 +416,9 @@ private static HeaderType validate(int streamId, CharSequence name, | |||
throw streamError(streamId, PROTOCOL_ERROR, | |||
"Illegal value specified for the 'TE' header (only 'trailers' is allowed)."); | |||
} | |||
if (!isValidHeaderValue(value)) { |
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.
I'm agreed that this validation is important and we should follow the spec. On the other hand, header values are tend to be quite long. Validating each value may be a significant performance hit. There are cases when communication is trusted between peers and such validation is not necessary. Can we consider an opt-out config options from this validation?
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.
We have a config for validating the headers. You want a separate one for header value validation?
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.
Yes, validation of the header-name is more important (they are more strict and most attackers target header names) and is used by many netty users by default (header names usually has more predictable length). If we add more validation for header values, we should let users preserve pre-existing behavior of validating only header names but not values.
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.
Actually, since the same validation rule applies for HTTP/2 and HTTP/1.x, have you considered moving this validation to the upper layer where it can be shared between two?
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.
ServiceTalk already does validation of the header values for both protocols, if netty also does it internally for HTTP/2 only without option to opt-out or share validation between HTTP/2 and HTTP/1.x, ServiceTalk users may end up doing expensive validation 2 times.
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.
I want to address that as a separate PR, because moving the validation around might have unpredictable effects on our existing tests.
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.
I would leave it up to @normanmaurer. The benefit of doing this now instead of a separate PR is the API impact. For example, if you end up moving values validation to DefaultHeaders
to share that between HTTP/2 and HTTP/1.X, you won't need any API changes for DefaultHttp2HeadersDecoder
made in this PR.
Btw, moving this validation to DefaultHeaders
has another benefit: users will be able to control it for inbound and outbound messages. In my practice, users frequently make mistakes in outbound header values, like adding \n
at the end of the value (especially when the value is pulled from somewhere). When an illegal header sent, they see a weird error at transport level that doesn't describe the cause for request failure. Very hard to debug unless you have a validation before sending the request that describes where is the mistake.
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
@chrisvest let me know once this is ready for re-review |
@normanmaurer This is ready for a second review now. |
codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java
Show resolved
Hide resolved
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
0d88318
to
f5715b8
Compare
codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDynamicTableTest.java
Outdated
Show resolved
Hide resolved
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java
Outdated
Show resolved
Hide resolved
codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoderTest.java
Outdated
Show resolved
Hide resolved
codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoderTest.java
Show resolved
Hide resolved
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.
- Exposed Used for testing only! Default values used in the initial settings frame are overridden intentionally
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.
Exposed used for testing only! Default values used in the initial settings frame are overridden intentionally
codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java
Outdated
Show resolved
Hide resolved
2bdfffb
to
72f5fe7
Compare
@normanmaurer @idelpivnitskiy I've consolidated the validation and pushed it down into the headers implementation, except for the validation that HTTP/2 headers are decoded pseudo-headers first, since I don't think we can compatibly place such a temporal restriction on the |
@@ -145,7 +145,7 @@ | |||
|
|||
@Test | |||
public void stripTEHeadersAccountsForOWS() { | |||
HttpHeaders inHeaders = new DefaultHttpHeaders(); | |||
HttpHeaders inHeaders = new DefaultHttpHeaders(false); |
Check warning
Code scanning / CodeQL
Disabled Netty HTTP header validation
codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValidationUtil.java
Outdated
Show resolved
Hide resolved
codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValidationUtil.java
Show resolved
Hide resolved
codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValidationUtil.java
Show resolved
Hide resolved
codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValidationUtil.java
Show resolved
Hide resolved
codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValidationUtil.java
Show resolved
Hide resolved
codec-http2/src/test/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerTest.java
Outdated
Show resolved
Hide resolved
codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDynamicTableTest.java
Outdated
Show resolved
Hide resolved
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.
LGTM!
codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java
Outdated
Show resolved
Hide resolved
…ng header names and values
a914a47
to
73d119f
Compare
@normanmaurer @idelpivnitskiy Addressed your comments. Please take a look. |
Ship it... |
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.
🚢
…aders Netty fixed the checks for HTTP/2 headers' compliance with RFC netty/netty#12760 This fixes the build against Netty 4.1 SNAPSHOT https://github.com/reactor/reactor-netty/actions/runs/3106167960
…aders (#2501) Netty fixed the checks for HTTP/2 headers' compliance with RFC netty/netty#12760 This fixes the build against Netty 4.1 SNAPSHOT https://github.com/reactor/reactor-netty/actions/runs/3106167960
Motivation: Netty 4.1.84 includes netty/netty#12760 which moves validation of connection-related headers for HTTP/2 from `HpackDecoder` to `DefaultHttp2Headers`. This breaks our existing control flow because in case there is no `content-length` header, we always unconditionally add `transfer-encoding: chunked` header that will be removed later in HTTP/2 pipeline if the HTTP/2 is the actual negotiated protocol. Modification: - Improve our control flow to take protocol into account when it adds `transfer-encoding: chunked` header; - Adjust existing tests that target `transfer-encoding: chunked` handling to always use `DefaultHttpHeadersFactory`, even when testing HTTP/2, it helps to avoid validation in netty's `DefaultHttp2Headers`; Result: All tests pass with both versions of netty: 4.1.82 and 4.1.84.
Motivation: netty#12755 added validation for presence of connection-related headers while `HpackDecoder` decodes the incoming frame. Then netty#12760 moved this validation from `HpackDecoder` to `DefaultHttp2Headers`. As the result, existing use-case that could use `DefaultHttp2Headers` for HTTP/2 and HTTP/1.X broke when users add any of the mentioned prohibited headers. The HTTP/1.X to HTTP/2 translation logic usually has sanitization process that removes connection-related headers. It's enough to run this validation only for incoming messages and we should preserve backward compatibility for 4.1. Modifications: - Move `isConnectionHeader` and `te` validations from `DefaultHttp2Headers` back to `HpackDecoder`; - Add tests to verify `HpackDecoder` fails incoming headers as expected; - Add tests to verify mentioned headers can be added to `DefaultHttp2Headers`; Result: Backward compatibility is preserved, while validation for connection-related headers is done in `HpackDecoder`.
#12975) Motivation: #12755 added validation for presence of connection-related headers while `HpackDecoder` decodes the incoming frame. Then #12760 moved this validation from `HpackDecoder` to `DefaultHttp2Headers`. As the result, existing use-case that could use `DefaultHttp2Headers` for HTTP/2 and HTTP/1.X broke when users add any of the mentioned prohibited headers. The HTTP/1.X to HTTP/2 translation logic usually has sanitization process that removes connection-related headers. It's enough to run this validation only for incoming messages and we should preserve backward compatibility for 4.1. Modifications: - Move `isConnectionHeader` and `te` validations from `DefaultHttp2Headers` back to `HpackDecoder`; - Add tests to verify `HpackDecoder` fails incoming headers as expected; - Add tests to verify mentioned headers can be added to `DefaultHttp2Headers`; Result: Backward compatibility is preserved, while validation for connection-related headers is done in `HpackDecoder`.
default: | ||
break; | ||
public void validate(CharSequence value) { | ||
int index = HttpHeaderValidationUtil.validateValidHeaderValue(value); |
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.
should the same validation rules be applied for both HTTP/1 and HTTP/2? This change introduces backward compatibility issues
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.
@chrisvest this looks like omission. value validation should be off by default for all protocols with opt-in flag. I see that for DefaultHttp2Headers
but not for `DefaultHttpHeaders
This PR builds upon #12755.
Motivation:
In https://datatracker.ietf.org/doc/html/rfc7540#section-10.3 it says that only certain characters are valid in a header value:
Modification:
Add a header value validation step to HpackDecoder.
Result:
Header values are now validated against the Section 10.3, etc. rules.