Skip to content

Metadata: Header value validation #11679

Open
@bananacocodrilo

Description

@bananacocodrilo

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 treats differently some invalid characters.

These are the rules for header values:

  1. ASCII-Value should not have leading or trailing whitespace.
  2. ASCII-Value → 1*( %x20-%x7E ) ; space and printable ASCII
  3. "Implementations must not error due to receiving an invalid ASCII-Value that's a valid field-value in HTTP, but the precise behaviour is not strictly defined: they may throw the value away or accept the value. If accepted, care must be taken to make sure that the application is permitted to echo the value back as metadata."

About rule 1 - ASCII-Value should not have leading or trailing whitespace.

We have not yet tested all the cases but it seems like neither Java nor Go are validating for this. Doesn't seem like there is a strong need to implement this.

About rule 2 - ASCII-Value → 1*( %x20-%x7E ) ; space and printable ASCII

The current metadata object in grpc-java 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 this are that depending on the type of invalid character, the headers have different behaviours:

  1. Characters outside of US_ASCII: The invalid character gets replaced by the replacement character (�), and after that substituted again for a question mark (?).
  2. Non printable characters inside of US_ASCII range (e.g: \t or \n) are not substituted. This leads to the header being removed from the request.

In grpc-go there is a straight forward validation of all the bytes before sending the request. If the validation fails the request call results in error.

About rule 3 - If accepted, care must be taken to make sure that the application is permitted to echo the value back as metadata.

Java uses the same validation as for request headers:

  • Characters outside of US_ASCII: The application code in the server will see the received header with the character substituted by the replacement character (�). When echoing back the header, the server will send the header with the character substituted by the question mark (?)
  • Non printable characters: The application code in the server will see those characters in the received headers but will drop the header from the response.

Go doesn't validate anything in the received headers nor in the response headers, so you are able to send back any header. You are also able to send any new headers that break all the rules in the response ( eg: "invalid-$":"Ĩñvá \t łið" )

Steps to reproduce the bug

Edit: Fixed misunderstanding from our side.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions