From 6cdb3ae33aabbbbc9214b9c9b568cd7ccfb685ef Mon Sep 17 00:00:00 2001 From: Manuel Bucher Date: Thu, 12 Aug 2021 09:34:20 +0000 Subject: [PATCH] Bug 1663836 - Treat invalid HTTP response header names over HTTP2 as malformed r=necko-reviewers,dragana Relevant section in the spec: https://datatracker.ietf.org/doc/html/rfc7540#section-10.3 Differential Revision: https://phabricator.services.mozilla.com/D120969 --- netwerk/protocol/http/Http2Compression.cpp | 12 ++++++++++++ netwerk/test/unit/test_http2.js | 11 +++++++++++ testing/xpcshell/moz-http2/moz-http2.js | 9 +++++++++ 3 files changed, 32 insertions(+) diff --git a/netwerk/protocol/http/Http2Compression.cpp b/netwerk/protocol/http/Http2Compression.cpp index ff9d6a6573b5..62aced440e4f 100644 --- a/netwerk/protocol/http/Http2Compression.cpp +++ b/netwerk/protocol/http/Http2Compression.cpp @@ -510,6 +510,18 @@ nsresult Http2Decompressor::OutputHeader(const nsACString& name, return NS_OK; } + // Bug 1663836: reject invalid HTTP response header names - RFC7540 Sec 10.3 + const char* cFirst = name.BeginReading(); + if (cFirst != nullptr && *cFirst == ':') { + ++cFirst; + } + if (!nsHttp::IsValidToken(cFirst, name.EndReading())) { + nsCString toLog(name); + LOG(("HTTP Decompressor invalid response header found. [%s]\n", + toLog.get())); + return NS_ERROR_ILLEGAL_VALUE; + } + // Look for upper case characters in the name. for (const char* cPtr = name.BeginReading(); cPtr && cPtr < name.EndReading(); ++cPtr) { diff --git a/netwerk/test/unit/test_http2.js b/netwerk/test/unit/test_http2.js index a02137d68989..d485afcb7aae 100644 --- a/netwerk/test/unit/test_http2.js +++ b/netwerk/test/unit/test_http2.js @@ -563,6 +563,16 @@ function test_http2_header() { chan.asyncOpen(listener); } +// Test to make sure headers with invalid characters in the name are rejected +function test_http2_invalid_response_header() { + var listener = new Http2CheckListener(); + listener.shouldSucceed = false; + var chan = makeChan( + "https://localhost:" + serverPort + "/invalid_response_header" + ); + chan.asyncOpen(listener); +} + // Test to make sure cookies are split into separate fields before compression function test_http2_cookie_crumbling() { var chan = makeChan("https://localhost:" + serverPort + "/cookie_crumbling"); @@ -1304,6 +1314,7 @@ var tests = [ test_http2_doubleheader, test_http2_xhr, test_http2_header, + test_http2_invalid_response_header, test_http2_cookie_crumbling, test_http2_multiplex, test_http2_big, diff --git a/testing/xpcshell/moz-http2/moz-http2.js b/testing/xpcshell/moz-http2/moz-http2.js index 4a6ab0ba4b4d..27571c345ee6 100644 --- a/testing/xpcshell/moz-http2/moz-http2.js +++ b/testing/xpcshell/moz-http2/moz-http2.js @@ -1678,6 +1678,15 @@ function handleRequest(req, res) { return; } + // response headers with invalid characters in the name + else if (u.pathname === "/invalid_response_header") { + res.setHeader("With Spaces", "Hello"); + res.setHeader("Without-Spaces", "World"); + res.writeHead(200); + res.end(""); + return; + } + res.setHeader("Content-Type", "text/html"); if (req.httpVersionMajor != 2) { res.setHeader("Connection", "close");