VYPR
Low severityOSV Advisory· Published Jan 5, 2026· Updated Jan 6, 2026

AIOHTTP Vulnerable to Cookie Parser Warning Storm

CVE-2025-69230

Description

AIOHTTP is an asynchronous HTTP client/server framework for asyncio and Python. In versions 3.13.2 and below, reading multiple invalid cookies can lead to a logging storm. If the cookies attribute is accessed in an application, then an attacker may be able to trigger a storm of warning-level logs using a specially crafted Cookie header. This issue is fixed in 3.13.3.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
aiohttpPyPI
< 3.13.33.13.3

Affected products

1

Patches

1
64629a0834f9

[PR #11890/384a1730 backport][3.13] Log once per cookie header (#11909)

https://github.com/aio-libs/aiohttppatchback[bot]Jan 3, 2026via ghsa
3 files changed · +38 11
  • aiohttp/_cookie_helpers.py+8 4 modified
    @@ -184,6 +184,7 @@ def parse_cookie_header(header: str) -> List[Tuple[str, Morsel[str]]]:
         i = 0
         n = len(header)
     
    +    invalid_names = []
         while i < n:
             # Use the same pattern as parse_set_cookie_headers to find cookies
             match = _COOKIE_PATTERN.match(header, i)
    @@ -201,9 +202,7 @@ def parse_cookie_header(header: str) -> List[Tuple[str, Morsel[str]]]:
     
                     # Validate the name (same as regex path)
                     if not _COOKIE_NAME_RE.match(key):
    -                    internal_logger.warning(
    -                        "Can not load cookie: Illegal cookie name %r", key
    -                    )
    +                    invalid_names.append(key)
                     else:
                         morsel = Morsel()
                         morsel.__setstate__(  # type: ignore[attr-defined]
    @@ -221,7 +220,7 @@ def parse_cookie_header(header: str) -> List[Tuple[str, Morsel[str]]]:
     
             # Validate the name
             if not key or not _COOKIE_NAME_RE.match(key):
    -            internal_logger.warning("Can not load cookie: Illegal cookie name %r", key)
    +            invalid_names.append(key)
                 continue
     
             # Create new morsel
    @@ -237,6 +236,11 @@ def parse_cookie_header(header: str) -> List[Tuple[str, Morsel[str]]]:
     
             cookies.append((key, morsel))
     
    +    if invalid_names:
    +        internal_logger.debug(
    +            "Cannot load cookie. Illegal cookie names: %r", invalid_names
    +        )
    +
         return cookies
     
     
    
  • tests/test_cookie_helpers.py+13 7 modified
    @@ -1,5 +1,6 @@
     """Tests for internal cookie helper functions."""
     
    +import logging
     import sys
     import time
     from http.cookies import (
    @@ -1444,14 +1445,16 @@ def test_parse_cookie_header_illegal_names(caplog: pytest.LogCaptureFixture) ->
         """Test parse_cookie_header warns about illegal cookie names."""
         # Cookie name with comma (not allowed in _COOKIE_NAME_RE)
         header = "good=value; invalid,cookie=bad; another=test"
    -    result = parse_cookie_header(header)
    +    with caplog.at_level(logging.DEBUG):
    +        result = parse_cookie_header(header)
         # Should skip the invalid cookie but continue parsing
         assert len(result) == 2
         assert result[0][0] == "good"
         assert result[0][1].value == "value"
         assert result[1][0] == "another"
         assert result[1][1].value == "test"
    -    assert "Can not load cookie: Illegal cookie name 'invalid,cookie'" in caplog.text
    +    assert "Cannot load cookie. Illegal cookie name" in caplog.text
    +    assert "'invalid,cookie'" in caplog.text
     
     
     def test_parse_cookie_header_large_value() -> None:
    @@ -1554,7 +1557,8 @@ def test_parse_cookie_header_invalid_name_in_fallback(
         """Test that fallback parser rejects cookies with invalid names."""
         header = 'normal=value; invalid,name={"x":"y"}; another=test'
     
    -    result = parse_cookie_header(header)
    +    with caplog.at_level(logging.DEBUG):
    +        result = parse_cookie_header(header)
     
         assert len(result) == 2
     
    @@ -1566,16 +1570,17 @@ def test_parse_cookie_header_invalid_name_in_fallback(
         assert name2 == "another"
         assert morsel2.value == "test"
     
    -    assert "Can not load cookie: Illegal cookie name 'invalid,name'" in caplog.text
    +    assert "Cannot load cookie. Illegal cookie name" in caplog.text
    +    assert "'invalid,name'" in caplog.text
     
     
     def test_parse_cookie_header_empty_key_in_fallback(
         caplog: pytest.LogCaptureFixture,
     ) -> None:
         """Test that fallback parser logs warning for empty cookie names."""
         header = 'normal=value; ={"malformed":"json"}; another=test'
    -
    -    result = parse_cookie_header(header)
    +    with caplog.at_level(logging.DEBUG):
    +        result = parse_cookie_header(header)
     
         assert len(result) == 2
     
    @@ -1587,7 +1592,8 @@ def test_parse_cookie_header_empty_key_in_fallback(
         assert name2 == "another"
         assert morsel2.value == "test"
     
    -    assert "Can not load cookie: Illegal cookie name ''" in caplog.text
    +    assert "Cannot load cookie. Illegal cookie name" in caplog.text
    +    assert "''" in caplog.text
     
     
     @pytest.mark.parametrize(
    
  • tests/test_web_request.py+17 0 modified
    @@ -1,5 +1,6 @@
     import asyncio
     import datetime
    +import logging
     import socket
     import time
     from collections.abc import MutableMapping
    @@ -380,6 +381,22 @@ def test_request_cookies_edge_cases() -> None:
         assert req.cookies == {"test": "quoted value", "normal": "unquoted"}
     
     
    +def test_request_cookies_many_invalid(caplog: pytest.LogCaptureFixture) -> None:
    +    """Test many invalid cookies doesn't cause too many logs."""
    +    bad = "bad" + chr(1) + "name"
    +    cookie = "; ".join(f"{bad}{i}=1" for i in range(3000))
    +    req = make_mocked_request("GET", "/", headers=CIMultiDict(COOKIE=cookie))
    +
    +    with caplog.at_level(logging.DEBUG):
    +        cookies = req.cookies
    +
    +    assert len(caplog.record_tuples) == 1
    +    _, level, msg = caplog.record_tuples[0]
    +    assert level is logging.DEBUG
    +    assert "Cannot load cookie" in msg
    +    assert cookies == {}
    +
    +
     def test_request_cookies_no_500_error() -> None:
         """Test that cookies with special characters don't cause 500 errors.
     
    

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

4

News mentions

0

No linked articles in our index yet.