Correctly handle whitespaces in HTTP header names as defined by RFC7230#section-3.2.4 (#9585)
Motivation: When parsing HTTP headers special care needs to be taken when a whitespace is detected in the header name. Modifications: - Ignore whitespace when decoding response (just like before) - Throw exception when whitespace is detected during parsing - Add unit tests Result: Fixes https://github.com/netty/netty/issues/9571
This commit is contained in:
parent
6c4a4a9658
commit
017a9658c9
@ -724,7 +724,21 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
|
||||
nameStart = findNonWhitespace(sb, 0);
|
||||
for (nameEnd = nameStart; nameEnd < length; nameEnd ++) {
|
||||
char ch = sb.charAtUnsafe(nameEnd);
|
||||
if (ch == ':' || Character.isWhitespace(ch)) {
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.2.4
|
||||
//
|
||||
// No whitespace is allowed between the header field-name and colon. In
|
||||
// the past, differences in the handling of such whitespace have led to
|
||||
// security vulnerabilities in request routing and response handling. A
|
||||
// server MUST reject any received request message that contains
|
||||
// whitespace between a header field-name and colon with a response code
|
||||
// of 400 (Bad Request). A proxy MUST remove any such whitespace from a
|
||||
// response message before forwarding the message downstream.
|
||||
if (ch == ':' ||
|
||||
// In case of decoding a request we will just continue processing and header validation
|
||||
// is done in the DefaultHttpHeaders implementation.
|
||||
//
|
||||
// In the case of decoding a response we will "skip" the whitespace.
|
||||
(!isDecodingRequest() && Character.isWhitespace(ch))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -320,4 +320,18 @@ public class HttpRequestDecoderTest {
|
||||
assertTrue(request.decoderResult().cause() instanceof TooLongFrameException);
|
||||
assertFalse(channel.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhitespace() {
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder());
|
||||
String requestStr = "GET /some/path HTTP/1.1\r\n" +
|
||||
"Transfer-Encoding : chunked\r\n" +
|
||||
"Host: netty.io\n\r\n";
|
||||
|
||||
assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII)));
|
||||
HttpRequest request = channel.readInbound();
|
||||
assertTrue(request.decoderResult().isFailure());
|
||||
assertTrue(request.decoderResult().cause() instanceof IllegalArgumentException);
|
||||
assertFalse(channel.finish());
|
||||
}
|
||||
}
|
||||
|
@ -663,4 +663,19 @@ public class HttpResponseDecoderTest {
|
||||
|
||||
assertFalse(channel.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhitespace() {
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseDecoder());
|
||||
String requestStr = "HTTP/1.1 200 OK\r\n" +
|
||||
"Transfer-Encoding : chunked\r\n" +
|
||||
"Host: netty.io\n\r\n";
|
||||
|
||||
assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII)));
|
||||
HttpResponse response = channel.readInbound();
|
||||
assertFalse(response.decoderResult().isFailure());
|
||||
assertEquals(HttpHeaderValues.CHUNKED.toString(), response.headers().get(HttpHeaderNames.TRANSFER_ENCODING));
|
||||
assertEquals("netty.io", response.headers().get(HttpHeaderNames.HOST));
|
||||
assertFalse(channel.finish());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user