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

Reject HTTP/2 header values with invalid characters #12760

Merged
merged 15 commits into from Sep 21, 2022

Conversation

chrisvest
Copy link
Contributor

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:

Any request or response that contains a character not permitted
in a header field value MUST be treated as malformed (Section 8.1.2.6).
Valid characters are defined by the "field-content" ABNF rule in
Section 3.2 of [RFC7230].

Modification:
Add a header value validation step to HpackDecoder.

Result:
Header values are now validated against the Section 10.3, etc. rules.

@normanmaurer
Copy link
Member

@ejona86 PTAL as well

@chrisvest
Copy link
Contributor Author

@normanmaurer Comments addressed.

@@ -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)) {
Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Member

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?

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

@normanmaurer
Copy link
Member

@chrisvest let me know once this is ready for re-review

@chrisvest
Copy link
Contributor Author

@normanmaurer This is ready for a second review now.

@chrisvest chrisvest marked this pull request as draft September 7, 2022 22:27
@chrisvest chrisvest removed this from the 4.1.81.Final milestone Sep 8, 2022
Copy link

@bodhili bodhili left a 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

Copy link

@bodhili bodhili left a 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

@chrisvest chrisvest force-pushed the 4.1-filter-header-values branch 2 times, most recently from 2bdfffb to 72f5fe7 Compare September 15, 2022 22:05
@chrisvest chrisvest marked this pull request as ready for review September 15, 2022 22:06
@chrisvest
Copy link
Contributor Author

@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 Http2Headers interface.

@@ -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

Request splitting or response splitting vulnerability due to header value verification being disabled.
Copy link
Member

@idelpivnitskiy idelpivnitskiy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@chrisvest
Copy link
Contributor Author

@normanmaurer @idelpivnitskiy Addressed your comments. Please take a look.

@normanmaurer
Copy link
Member

Ship it...

Copy link
Member

@idelpivnitskiy idelpivnitskiy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚢

@chrisvest chrisvest merged commit 980f48a into netty:4.1 Sep 21, 2022
@chrisvest chrisvest deleted the 4.1-filter-header-values branch September 21, 2022 16:10
@chrisvest chrisvest added this to the 4.1.83.Final milestone Sep 21, 2022
violetagg added a commit to reactor/reactor-netty that referenced this pull request Sep 23, 2022
…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
violetagg added a commit to reactor/reactor-netty that referenced this pull request Sep 23, 2022
…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
idelpivnitskiy added a commit to apple/servicetalk that referenced this pull request Oct 27, 2022
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.
idelpivnitskiy added a commit to idelpivnitskiy/netty that referenced this pull request Nov 8, 2022
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`.
normanmaurer pushed a commit that referenced this pull request Nov 9, 2022
#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);

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

Copy link
Member

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

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

Successfully merging this pull request may close these issues.

None yet

5 participants