From: Ben Darnell Date: Thu, 20 Mar 2025 21:59:56 +0000 (-0400) Subject: httputil: Enforce RFC rules for header names X-Git-Tag: v6.5.0b1~21^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F3469%2Fhead;p=thirdparty%2Ftornado.git httputil: Enforce RFC rules for header names Previously we allowed nearly any string in header names, but RFC 9110 restricts them to certain printable ASCII characters. Fixes #3310 Fixes #2790 --- diff --git a/tornado/httputil.py b/tornado/httputil.py index 899d76ae..536b1455 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -71,6 +71,8 @@ else: # To be used with str.strip() and related methods. HTTP_WHITESPACE = " \t" +HTTP_TOKEN_RE = re.compile(r"^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$") + @lru_cache(1000) def _normalize_header(name: str) -> str: @@ -143,6 +145,8 @@ class HTTPHeaders(StrMutableMapping): def add(self, name: str, value: str) -> None: """Adds a new value for the given key.""" + if not HTTP_TOKEN_RE.match(name): + raise HTTPInputError("Invalid header name %r" % name) norm_name = _normalize_header(name) self._last_key = norm_name if norm_name in self: @@ -859,7 +863,7 @@ def parse_multipart_form_data( def format_timestamp( - ts: Union[int, float, tuple, time.struct_time, datetime.datetime] + ts: Union[int, float, tuple, time.struct_time, datetime.datetime], ) -> str: """Formats a timestamp in the format used by HTTP. diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py index 46a28bc6..53ae4607 100644 --- a/tornado/test/httputil_test.py +++ b/tornado/test/httputil_test.py @@ -409,6 +409,22 @@ Foo: even headers2 = HTTPHeaders.parse(str(headers)) self.assertEqual(headers, headers2) + def test_invalid_header_names(self): + invalid_names = [ + "", + "foo bar", + "foo\tbar", + "foo\nbar", + "foo\x00bar", + "foo ", + " foo", + "é", + ] + for name in invalid_names: + headers = HTTPHeaders() + with self.assertRaises(HTTPInputError): + headers.add(name, "bar") + class FormatTimestampTest(unittest.TestCase): # Make sure that all the input types are supported.