From bc7e2ac5fa4be8efc74b9b86321f886e613a7066 Mon Sep 17 00:00:00 2001 From: Ben Darnell Date: Thu, 20 Mar 2025 17:59:56 -0400 Subject: [PATCH] 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 --- tornado/httputil.py | 6 +++++- tornado/test/httputil_test.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) 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. -- 2.47.2