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

Metadata: Header value validation #11679

Open
bananacocodrilo opened this issue Nov 8, 2024 · 0 comments · May be fixed by #11696
Open

Metadata: Header value validation #11679

bananacocodrilo opened this issue Nov 8, 2024 · 0 comments · May be fixed by #11696

Comments

@bananacocodrilo
Copy link

bananacocodrilo commented Nov 8, 2024

What version of gRPC-Java are you using?

1.41.x, but also observed in master branch

What is your environment?

Linux/MacOs, n/a

What did you expect to see?

Metadata validation is consistent between Java and Go implementations.

What did you see instead?

Java allows some invalid characters.

These are the rules for header values:

  • ASCII-Value → 1*( %x20-%x7E ) ; space and printable ASCII
  • ASCII-Value should not have leading or trailing whitespace

The current validation relies on encoded.getBytes(US_ASCII); :

byte[] toBytes(T value) {
String encoded = Preconditions.checkNotNull(
marshaller.toAsciiString(value), "null marshaller.toAsciiString()");
return encoded.getBytes(US_ASCII);
}

public <T> void put(Key<T> key, T value) {
Preconditions.checkNotNull(key, "key");
Preconditions.checkNotNull(value, "value");
maybeExpand();
name(size, key.asciiName());
if (key.serializesToStreams()) {
value(size, LazyValue.create(key, value));
} else {
value(size, key.toBytes(value));
}
size++;
}

The effects of validating this way are:
1: For header values that contain characters outside of US_ASCII: The invalid character gets replaced by the replacement character �, and after that substituted again for a question mark "?".
2: For header values that contain invalid characters inside of US_ASCII range (e.g: \t or \n): They are kept as they are.

This doesn't align with Go's (stricter) implementation of the validation:

https://github.com/grpc/grpc-go/blob/a3a8657078983be4c5e9e9e856c757a5de9b3a45/internal/metadata/metadata.go#L106

Here, the correct range of character is validated, and requests fail if there are invalid characters (instead of replacing it with �). But the validation only occurs for headers transmitted, (both req and res), not for headers received.

This creates a number of problems:

  1. Servers will receive different values than what the application sent.
  • A header test-invalid:José sent by a Java client becomes test-invalid:Jos� and finally test-invalid:Jos? before being sent out.
  • A header test-invalid:José received by a Java server becomes test-invalid:Jos? before reaching the application code.
  1. If a Go server needs to forward headers originated in a Java client, it can lead to requests failing in the Go server. E.g.: test-invalid-2:a\ta will pass validation in Java, be sent to the Go server, and then Go will fail the validation when transmitting it either in the response or in requests to downstreams.

Steps to reproduce the bug

1: Request with header test-invalid:José will be altered to test-invalid:Jos?
2: Header values that contain invalid characters inside of US_ASCII: They are kept as they are.

Edit: minor detail in how encoded.getBytes() works.

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 a pull request may close this issue.

2 participants