Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit feee8be

Browse files
committedAug 26, 2019
Add a ZeroLengthHeaderError raised if header name is 0-length
This is to mitigate CVE-2019-9516, 0-Length Headers Leak. It will allow hpack users, such as hyper-h2, to close connections if this happens on the basis that the client is likely attempting a DoS attack.
1 parent ec8c671 commit feee8be

File tree

5 files changed

+45
-4
lines changed

5 files changed

+45
-4
lines changed
 

‎HISTORY.rst

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ Release History
66

77
**API Changes (Backward Compatible)**
88

9+
**Security Fixes**
10+
11+
- CVE-2019-9516: 0-Length Headers Leak. This release now enforces that
12+
headers are not zero length. This is to avoid an attack whereby an
13+
attacker sends a stream of headers with a 0-length header name in
14+
order to consume server resources cheaply.
15+
16+
This also adds a ``ZeroLengthHeaderError``, which is thrown by the
17+
``decode`` method if any header name is zero length. This is ok as
18+
empty header names are forbidden. This places the HPACK decoder into
19+
a broken state: it must not be used after this exception is thrown.
20+
921
**Bugfixes**
1022

1123
- Performance improvement of static header search. Use dict search instead

‎hpack/__init__.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
from .hpack import Encoder, Decoder
99
from .struct import HeaderTuple, NeverIndexedHeaderTuple
1010
from .exceptions import (
11-
HPACKError, HPACKDecodingError, InvalidTableIndex, OversizedHeaderListError
11+
HPACKError, HPACKDecodingError, InvalidTableIndex,
12+
OversizedHeaderListError, ZeroLengthHeaderError,
1213
)
1314

1415
__all__ = [
1516
'Encoder', 'Decoder', 'HPACKError', 'HPACKDecodingError',
1617
'InvalidTableIndex', 'HeaderTuple', 'NeverIndexedHeaderTuple',
17-
'OversizedHeaderListError'
18+
'OversizedHeaderListError', 'ZeroLengthHeaderError',
1819
]
1920

2021
__version__ = '3.1.0dev0'

‎hpack/exceptions.py

+9
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,12 @@ class InvalidTableSizeError(HPACKDecodingError):
4747
.. versionadded:: 3.0.0
4848
"""
4949
pass
50+
51+
52+
class ZeroLengthHeaderError(HPACKDecodingError):
53+
"""
54+
A zero length header has been received. This may be a DoS attack.
55+
56+
.. versionadded:: 3.1.0
57+
"""
58+
pass

‎hpack/hpack.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from .table import HeaderTable, table_entry_size
1111
from .compat import to_byte, to_bytes
1212
from .exceptions import (
13-
HPACKDecodingError, OversizedHeaderListError, InvalidTableSizeError
13+
HPACKDecodingError, OversizedHeaderListError, InvalidTableSizeError,
14+
ZeroLengthHeaderError,
1415
)
1516
from .huffman import HuffmanEncoder
1617
from .huffman_constants import (
@@ -496,6 +497,7 @@ def decode(self, data, raw=False):
496497

497498
if header:
498499
headers.append(header)
500+
self._assert_valid_header_size(header)
499501
inflated_size += table_entry_size(*header)
500502

501503
if inflated_size > self.max_header_list_size:
@@ -516,6 +518,13 @@ def decode(self, data, raw=False):
516518
except UnicodeDecodeError:
517519
raise HPACKDecodingError("Unable to decode headers as UTF-8.")
518520

521+
def _assert_valid_header_size(self, header):
522+
"""
523+
Check that the header size is valid, i.e. non-zero.
524+
"""
525+
if len(header[0]) == 0:
526+
raise ZeroLengthHeaderError()
527+
519528
def _assert_valid_table_size(self):
520529
"""
521530
Check that the table size set by the encoder is lower than the maximum

‎test/test_hpack.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from hpack.hpack import Encoder, Decoder, _dict_to_iterable, _to_bytes
33
from hpack.exceptions import (
44
HPACKDecodingError, InvalidTableIndex, OversizedHeaderListError,
5-
InvalidTableSizeError
5+
InvalidTableSizeError, ZeroLengthHeaderError,
66
)
77
from hpack.struct import HeaderTuple, NeverIndexedHeaderTuple
88
import itertools
@@ -638,6 +638,16 @@ def test_max_header_list_size(self):
638638
with pytest.raises(OversizedHeaderListError):
639639
d.decode(data)
640640

641+
def test_zero_length_header(self):
642+
"""
643+
If a header has a name of zero length it is invalid and the HPACK
644+
decoder raises a ZeroLengthHeaderError.
645+
"""
646+
d = Decoder(max_header_list_size=44)
647+
data = b"@\x80\x80"
648+
with pytest.raises(ZeroLengthHeaderError):
649+
d.decode(data)
650+
641651
def test_can_decode_multiple_header_table_size_changes(self):
642652
"""
643653
If multiple header table size changes are sent in at once, they are

0 commit comments

Comments
 (0)
Please sign in to comment.