VYPR
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.

PackageAffected versionsPatched versions
h2PyPI
< 4.3.04.3.0

Affected products

1

Patches

2
1aae569315eb

v4.3.0

https://github.com/python-hyper/h2Thomas KriechbaumerAug 23, 2025via osv
2 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"
    
035e9899f95e

be stricter about which characters to accept for headers

https://github.com/python-hyper/h2Maximilian HilsAug 19, 2025via ghsa
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

News mentions

0

No linked articles in our index yet.