Skip to content

Commit c9231da

Browse files
committed
reject empty header names
fixes #1257
1 parent 13589e3 commit c9231da

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

Diff for: src/h2/utilities.py

+15
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,9 @@ def validate_headers(headers, hdr_validation_flags):
206206
# For example, we avoid tuple unpacking in loops because it represents a
207207
# fixed cost that we don't want to spend, instead indexing into the header
208208
# tuples.
209+
headers = _reject_empty_header_names(
210+
headers, hdr_validation_flags
211+
)
209212
headers = _reject_uppercase_header_fields(
210213
headers, hdr_validation_flags
211214
)
@@ -229,6 +232,18 @@ def validate_headers(headers, hdr_validation_flags):
229232
return headers
230233

231234

235+
def _reject_empty_header_names(headers, hdr_validation_flags):
236+
"""
237+
Raises a ProtocolError if any header names are empty (length 0).
238+
While hpack decodes such headers without errors, they are semantically
239+
forbidden in HTTP, see RFC 7230, stating that they must be at least one
240+
character long.
241+
"""
242+
for header in headers:
243+
if len(header[0]) == 0:
244+
raise ProtocolError("Received header name with zero length.")
245+
yield header
246+
232247
def _reject_uppercase_header_fields(headers, hdr_validation_flags):
233248
"""
234249
Raises a ProtocolError if any uppercase character is found in a header

Diff for: test/test_invalid_headers.py

+18
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,24 @@ def test_inbound_resp_header_extra_pseudo_headers(self,
688688
with pytest.raises(h2.exceptions.ProtocolError):
689689
list(h2.utilities.validate_headers(headers, hdr_validation_flags))
690690

691+
@pytest.mark.parametrize('hdr_validation_flags', hdr_validation_combos)
692+
def test_inbound_header_name_length(self, hdr_validation_flags):
693+
with pytest.raises(h2.exceptions.ProtocolError):
694+
list(h2.utilities.validate_headers([(b'', b'foobar')], hdr_validation_flags))
695+
696+
def test_inbound_header_name_length_full_frame_decode(self, frame_factory):
697+
f = frame_factory.build_headers_frame([])
698+
f.data = b"\x00\x00\x05\x00\x00\x00\x00\x04"
699+
data = f.serialize()
700+
701+
c = h2.connection.H2Connection(config=h2.config.H2Configuration(client_side=False))
702+
c.initiate_connection()
703+
c.receive_data(frame_factory.preamble())
704+
c.clear_outbound_data_buffer()
705+
706+
with pytest.raises(h2.exceptions.ProtocolError, match="Received header name with zero length."):
707+
c.receive_data(data)
708+
691709

692710
class TestOversizedHeaders(object):
693711
"""

0 commit comments

Comments
 (0)