fix: HTTP request smuggling via Transfer-Encoding token whitespace
Closes https://github.com/dart-lang/sdk/pull/63250 GitOrigin-RevId: 7c6b7bf7d033dd31dc23a7cc3a30b3dc4db41d16 Change-Id: I573328660bbef7ec9cd829ec376fcee894a6bc34 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/498420 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Slava Egorov <vegorov@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
1a7e73a7e5
commit
c5d23e0df3
@@ -1112,20 +1112,31 @@ class _HttpParser extends Stream<_HttpIncoming> {
|
||||
value.length = length;
|
||||
}
|
||||
|
||||
static bool _isOWS(int c) {
|
||||
return c == _CharCode.SP || c == _CharCode.HT;
|
||||
}
|
||||
|
||||
static List<String> _tokenizeFieldValue(String headerValue) {
|
||||
int length = headerValue.length;
|
||||
List<String> tokens = <String>[];
|
||||
// First non-OWS character of the token. Note that headerValue is already
|
||||
// trimmed from start and end so we don't need to skip characters at start.
|
||||
int start = 0;
|
||||
int index = 0;
|
||||
while (index < headerValue.length) {
|
||||
if (headerValue[index] == ",") {
|
||||
tokens.add(headerValue.substring(start, index));
|
||||
start = index + 1;
|
||||
} else if (headerValue[index] == " " || headerValue[index] == "\t") {
|
||||
while (start < length) {
|
||||
int end = start + 1;
|
||||
while (end < length && headerValue.codeUnitAt(end) != _CharCode.COMMA) {
|
||||
end++;
|
||||
}
|
||||
int comma = end;
|
||||
while (start < end && _isOWS(headerValue.codeUnitAt(end - 1))) {
|
||||
end--;
|
||||
}
|
||||
tokens.add(headerValue.substring(start, end));
|
||||
start = comma + 1;
|
||||
while (start < length && _isOWS(headerValue.codeUnitAt(start))) {
|
||||
start++;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
tokens.add(headerValue.substring(start, index));
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
||||
@@ -718,6 +718,27 @@ Content-Length: 38\r
|
||||
GET /admin HTTP/1.1\r\nHost: backend\r\n\r\n""";
|
||||
_testParseInvalidRequest(request);
|
||||
|
||||
// RFC 7230 section 3.2.6 forbids SP/HT inside a token. With the tokenizer
|
||||
// treating "x chunked" as a single token (not "chunked"), the request has
|
||||
// a Transfer-Encoding header whose final coding is not chunked, so it
|
||||
// MUST be rejected (RFC 7230 section 3.3.3 rule 3) rather than falling
|
||||
// back to Content-Length.
|
||||
request = """
|
||||
POST /test HTTP/1.1\r
|
||||
Content-Length: 10\r
|
||||
Transfer-Encoding: x chunked\r
|
||||
\r
|
||||
0123456789""";
|
||||
_testParseInvalidRequest(request);
|
||||
|
||||
request = """
|
||||
POST /test HTTP/1.1\r
|
||||
Content-Length: 10\r
|
||||
Transfer-Encoding: x\tchunked\r
|
||||
\r
|
||||
0123456789""";
|
||||
_testParseInvalidRequest(request);
|
||||
|
||||
// Content-Length and "Transfer-Encoding: chunked" are specified.
|
||||
request = """
|
||||
POST /test HTTP/1.1\r
|
||||
|
||||
Reference in New Issue
Block a user