Medium severityOSV Advisory· Published Aug 25, 2025· Updated Apr 15, 2026
CVE-2025-57804
CVE-2025-57804
Description
h2 is a pure-Python implementation of a HTTP/2 protocol stack. Prior to version 4.3.0, an HTTP/2 request splitting vulnerability allows attackers to perform request smuggling attacks by injecting CRLF characters into headers. This occurs when servers downgrade HTTP/2 requests to HTTP/1.1 without properly validating header names/values, enabling attackers to manipulate request boundaries and bypass security controls. This issue has been patched in version 4.3.0.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
h2PyPI | < 4.3.0 | 4.3.0 |
Affected products
1- Range: v1.0.0, v1.1.0, v1.1.1, …
Patches
22 files changed · +9 −5
CHANGELOG.rst+8 −4 modified@@ -1,21 +1,25 @@ Release History =============== -dev ---- +4.3.0 (2025-08-23) +------------------ **API Changes (Backward Incompatible)** -- Reject header names and values containing unpermitted characters `\r`, `\n`, or `\0x00`. +- Reject header names and values containing illegal characters, based on RFC 9113, section 8.2.1. + The main Python API is compatible, but some previously valid requests/response headers might now be blocked. + Use the `validate_inbound_headers` config option if needed. + Thanks to Sebastiano Sartor (sebsrt) for the report. **API Changes (Backward Compatible)** - h2 events now have tighter type bounds, e.g. `stream_id` is guaranteed to not be `None` for most events now. This simplifies downstream type checking. +- Various typing-related improvements. **Bugfixes** -- +- Fix error value when opening a new stream on too many open streams. 4.2.0 (2025-02-01) ------------------
src/h2/__init__.py+1 −1 modified@@ -3,4 +3,4 @@ """ from __future__ import annotations -__version__ = "4.3.0+dev" +__version__ = "4.3.0"
035e9899f95ebe stricter about which characters to accept for headers
2 files changed · +33 −22
src/h2/utilities.py+25 −16 modified@@ -24,12 +24,6 @@ SIGIL = ord(b":") INFORMATIONAL_START = ord(b"1") -HEADER_UNPERMITTED_CHARACTERS = frozenset([ - b"\r", - b"\n", - b"\x00", -]) - # A set of headers that are hop-by-hop or connection-specific and thus # forbidden in HTTP/2. This list comes from RFC 7540 § 8.1.2.2. @@ -207,7 +201,7 @@ def validate_headers(headers: Iterable[Header], hdr_validation_flags: HeaderVali # For example, we avoid tuple unpacking in loops because it represents a # fixed cost that we don't want to spend, instead indexing into the header # tuples. - headers = _reject_unpermitted_characters( + headers = _reject_illegal_characters( headers, hdr_validation_flags, ) headers = _reject_empty_header_names( @@ -234,20 +228,35 @@ def validate_headers(headers: Iterable[Header], hdr_validation_flags: HeaderVali return _check_path_header(headers, hdr_validation_flags) -def _reject_unpermitted_characters(headers: Iterable[Header], - hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]: +def _reject_illegal_characters(headers: Iterable[Header], + hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]: """ - Raises a ProtocolError if any header names or values contain unpermitted characters. - See RFC 7540, section 10.3 and 8.1.2.6. + Raises a ProtocolError if any header names or values contain illegal characters. + See RFC 9113, section 8.2.1. """ for header in headers: - for c in HEADER_UNPERMITTED_CHARACTERS: - if c in header[0]: - msg = f"Unpermitted character '{c}' in header name: {header[0]!r}" + # > A field name MUST NOT contain characters in the ranges 0x00-0x20, 0x41-0x5a, + # > or 0x7f-0xff (all ranges inclusive). + for c in header[0]: + if c <= 0x20 or 0x41 <= c <= 0x5a or 0x7f <= c: + msg = f"Illegal character '{chr(c)}' in header name: {header[0]!r}" raise ProtocolError(msg) - if c in header[1]: - msg = f"Unpermitted character '{c}' in header value: {header[1]!r}" + + # > With the exception of pseudo-header fields (Section 8.3), which have a name + # > that starts with a single colon, field names MUST NOT include a colon (ASCII + # > COLON, 0x3a). + if header[0].find(b":", 1) != -1: + msg = f"Illegal character ':' in header name: {header[0]!r}" + raise ProtocolError(msg) + + # > A field value MUST NOT contain the zero value (ASCII NUL, 0x00), line feed + # > (ASCII LF, 0x0a), or carriage return (ASCII CR, 0x0d) at any position. + for c in header[1]: + if c == 0 or c == 0x0a or c == 0x0d: + msg = f"Illegal character '{chr(c)}' in header value: {header[1]!r}" raise ProtocolError(msg) + + # Surrounding whitespace is enforced in `_reject_surrounding_whitespace`. yield header
tests/test_invalid_headers.py+8 −6 modified@@ -48,12 +48,14 @@ class TestInvalidFrameSequences: [*base_request_headers, ("name ", "name with trailing space")], [*base_request_headers, ("name", " value with leading space")], [*base_request_headers, ("name", "value with trailing space ")], - [*base_request_headers, ("unpermitted-\r-characters", "value")], - [*base_request_headers, ("unpermitted-\n-characters", "value")], - [*base_request_headers, ("unpermitted-\x00-characters", "value")], - [*base_request_headers, ("unpermitted-characters", "some \r value")], - [*base_request_headers, ("unpermitted-characters", "some \n value")], - [*base_request_headers, ("unpermitted-characters", "some \x00 value")], + [*base_request_headers, ("illegal:characters", "value")], + [*base_request_headers, ("illegal-\r-characters", "value")], + [*base_request_headers, ("illegal-\n-characters", "value")], + [*base_request_headers, ("illegal-\x00-characters", "value")], + [*base_request_headers, ("illegal-\x01-characters", "value")], + [*base_request_headers, ("illegal-characters", "some \r value")], + [*base_request_headers, ("illegal-characters", "some \n value")], + [*base_request_headers, ("illegal-characters", "some \x00 value")], [header for header in base_request_headers if header[0] != ":authority"], [(":protocol", "websocket"), *base_request_headers],
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-847f-9342-265hghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-57804ghsaADVISORY
- github.com/python-hyper/h2/commit/035e9899f95e3709af098f578bfc3cd302298e3anvdWEB
- github.com/python-hyper/h2/security/advisories/GHSA-847f-9342-265hnvdWEB
- lists.debian.org/debian-lts-announce/2025/09/msg00004.htmlnvdWEB
News mentions
0No linked articles in our index yet.