-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Fixing GRPC initial metadata validation #16414
Conversation
@htuch for your review |
// ASCII header | ||
} else { | ||
for (auto ch : header.value()) { | ||
if (!(absl::ascii_isascii(ch))) { |
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 don't think this check is sufficient: absl::ascii_isascii(ch) returns true if ch < 128 (see here: http://docs.ros.org/en/kinetic/api/abseil_cpp/html/ascii_8h_source.html). GRPC spec? (https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md) defines ascii char as one between %x20-%x7E.
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.
Ok, I can change the check
if (!validateGrpcHeaderChars(header.key()) || !validateGrpcHeaderChars(header.value())) { | ||
throw EnvoyException("Illegal characters in gRPC initial metadata."); | ||
// Validate key | ||
if (!validateGrpcHeaderChars(header.key())) { |
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.
If I'm reading the spec right, header names can be lower-case ascii chars, numericals, _
, -
, and .
. validateGrpcHeaderChars
will return true
if the parameter is an upper-case char (try running python -c 'print("A".isalnum())'
. Abseil's kPropertyBits
is generated using python script, according to comments in ascii.c. kPropertyBits
is then used to verify if a character is an alpha-numeric one),
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.
So I'm not sure - but according to this: https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#creating-a-new-metadata it sounds like you can define initial metadata to be both lower and upercase but it will be converted to lowercase. If you think it's better to have a stricter check I can do so but I'm not sure it's the correct intent in this case.
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.
FYI, HTTP/2 always sends header names as lower-case on the wire:
|
||
// Validate value | ||
// Binary base64 encoded | ||
if (::absl::EndsWith(header.key(), "-bin")) { |
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.
@markdroth can you also take a look at this? Thanks.
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.
If I'm understanding this code right, it looks like this is requiring that the metadata be already base64 encoded. I don't think that's necessary for GOOGLE_GRPC, because the gRPC library will automatically do base64 encoding and decoding for header values if the header name ends with "-bin".
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.
What happens if it is already base64 encoded?
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.
If the gRPC library automatically does encoding/decoding - what check should be done in this case? or is any value allowed?
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 don't check to see if the value is already base64-encoded; we just unconditionally base64-encode it. So it would wind up being double-base64-encoded on the wire.
I think this will actually work, but it does add unnecessary overhead.
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.
Then we can probably allow any value for -bin
. This check was added in #14129 (CC @asraa) because fuzzers were crashing the gRPC library with inputs that looked like https://github.com/envoyproxy/envoy/pull/14129/files#diff-2a20e9888f070730d0412bf9806728cfae9f9f9269747837f68287ff4fcdb5caR1.
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.
That seems right, any value probably goes for -bin
.
Otherwise you would have seen a crash in the grpc library in the test GoogleGrpcIllegalLengthBase64 if you didn't catch with the exception below...
On the PR as a whole, thanks for correcting this check!
Updated the code:
I did not modify the lower/upper case of the header key |
} | ||
} | ||
return true; | ||
|
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.
Nit: superfluous blank. If you really want you can use return std::all_of
style here, but it's a bit of a wash.
if (::absl::EndsWith(header.key(), "-bin")) { | ||
continue; | ||
// ASCII header | ||
} else { |
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.
Nit: you can probably collapse this down to a single if
predicate with &&
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 modulo nits.
I think |
@dmitri-d I think it's fine to have input being lower/upper mix; it looks like the C++ gRPC library is robust to this as this was also the previous validation. I'd suspect it just gets forced lower case when put on the wire. @omriz this need a merge to fix format concerns (see CI). Otherwise ready to ship. |
/retest |
Retrying Azure Pipelines: |
According to the GRPC spec https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md The values in the initial metadata can be any valid ASCII character in case of a non binary header type. Signed-off-by: Omri Zohar <omriz@google.com>
/retest |
Retrying Azure Pipelines: |
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, thanks!
@omriz for future it is generally preferable to merge main, avoid rebase and force push. This allows faster validation of deltas between commits.
According to the GRPC spec https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md The values in the initial metadata can be any valid ASCII character in case of a non binary header type. Risk Level: Low Testing: Unit Tests Docs Changes:N/A Release Notes: Fixing GRPC initial metadata validation for ASCII characters Signed-off-by: Omri Zohar <omriz@google.com>
Commit Message: Fixing GRPC initial metadata validation
Additional Description:
According to the GRPC spec https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
The values in the initial metadata can be any valid ASCII character in
case of a non binary header type.
Risk Level: Low
Testing: Unit Tests
Docs Changes:N/A
Release Notes: Fixing GRPC initial metadata validation for ASCII characters
Platform Specific Features: None