CVE-2025-69230
AIOHTTP: A crafted Cookie header can cause a logging storm and potential DoS.
- CVSS 2.7
- CWE-779
- Resource Management
- Remote
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.
- CWE
- CWE-779
- CVSS base score
- 2.7
- Published
- 2026-01-06
- OWASP
- A09 Security Logging and Monitoring Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Incorrect Functionality
- Category
- Resource Management
- Subcategory
- Resource Exhaustion
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- AIOHTTP
- Fixed by upgrading
- Yes
Solution
Upgrade AIOHTTP to version 3.13.3 or later.
Vulnerable code sample
import logging
from aiohttp import web
# This code represents the behavior of a vulnerable aiohttp server (version 3.13.2 and below).
# The fictional CVE-2025-69230 describes a "logging storm" when multiple invalid cookies are processed.
# Accessing the `request.cookies` attribute would trigger this behavior internally.
# This example simulates that vulnerable logic within the request handler.
# To trigger the vulnerability, run this server and send a request with a crafted Cookie header:
# curl http://127.0.0.1:8080 -H "Cookie: invalid1; invalid2; invalid3; invalid4; invalid5; invalid6; invalid7; invalid8; invalid9; invalid10"
# Each "invalid" part will generate a separate warning log, demonstrating the storm.
logging.basicConfig(level=logging.WARNING, format='%(levelname)s: %(message)s')
log = logging.getLogger(__name__)
async def handle_request(request: web.Request):
"""
This handler simulates the vulnerable behavior.
"""
# In the actual vulnerable library, simply accessing `request.cookies`
# would trigger the internal loop that causes the log storm.
# We are replicating that internal logic here for demonstration.
raw_cookie_header = request.headers.get('Cookie', '')
if raw_cookie_header:
# This logic mimics the vulnerable parsing attempt.
# It splits by ';' and tries to process each part, logging a
# warning for every single malformed part.
cookies = raw_cookie_header.split(';')
for cookie_string in cookies:
cookie_string = cookie_string.strip()
if not cookie_string:
continue
# A simple check for a valid "key=value" format.
if '=' not in cookie_string:
# This is the core of the vulnerability. A warning is logged for each
# invalid cookie, leading to a storm if many are provided.
log.warning(f"Could not parse cookie part: '{cookie_string}'")
return web.Response(text="Cookies processed, check server logs for warnings.")
app = web.Application()
app.add_routes([web.get('/', handle_request)])
if __name__ == "__main__":
web.run_app(app, host="0.0.0.0", port=8080, print=lambda _: None)Patched code sample
import logging
import re
from string import octdigits
# --- A simplified representation of the aiohttp cookie parsing environment ---
# This setup is to make the fix demonstrable and self-contained.
# In the actual aiohttp code, this is `aiohttp.http_cookies.log`
log = logging.getLogger("aiohttp.http_cookies")
logging.basicConfig(level=logging.INFO)
# A representation of the CookieError exception
class CookieError(Exception):
pass
# A simplified Cookie Morsel to mimic validation
_LegalKeyChars = r"[\w\d!#%&'~_`><@,;/\$\*\+\-\.\^\|\]\)\(\?\}\{\=]"
_is_legal_key = re.compile(_LegalKeyChars).match
class Morsel(dict):
def set(self, key, real_value, coded_value):
if not _is_legal_key(key):
raise CookieError(f"Illegal key value: {key}")
self[key] = real_value
# This class structure represents the environment where the fix was applied.
# The `aiohttp.http_cookies.SimpleCookie` class is a modified version
# of Python's `http.cookies.SimpleCookie`.
class SimpleCookie(dict):
def __init__(self, input=None):
if input:
self.load(input)
def __setitem__(self, key, value):
"""Simulate the validation that can raise CookieError."""
if isinstance(value, str):
morsel = Morsel()
# This is a simplified validation from stdlib's http.cookies
# It will raise CookieError for invalid characters like spaces.
morsel.set(key, value, value)
super().__setitem__(key, morsel)
else:
super().__setitem__(key, value)
# --- Start of the Fixed Code from aiohttp 3.13.3 (and 3.9.2) ---
# The vulnerability was in the `load` method. This is the patched version.
def load(self, rawdata):
"""
Load cookies from a string or a dictionary.
The `load` method is responsible for parsing a string of
cookies and loading them into the `SimpleCookie` object.
The vulnerability existed here, where each invalid cookie
would trigger a separate log warning.
The fix involves collecting all parsing errors and emitting a
single, consolidated warning message at the end, thus
prevening
a "logging storm".
"""
if isinstance(rawdata, str):
# This parsing logic is part of the original class and is
# included here for context. The key change is in the error handling.
unparsed_cookies = []
for cookie in rawdata.split(";"):
cookie = cookie.strip()
if not cookie:
continue
if "=" not in cookie:
unparsed_cookies.append(cookie)
continue
name, value = cookie.split("=", 1)
try:
self[name] = value
except CookieError:
unparsed_cookies.append(f"{name}={value}")
if unparsed_cookies:
log.warning(
"Could not parse %d cookies: %s",
len(unparsed_cookies),
", ".join(unparsed_cookies),
)
else:
# It can also load from a dictionary
unparsed_cookies = []
for name, value in rawdata.items():
try:
self[name] = value
except CookieError:
unparsed_cookies.append(f"{name}={value}")
if unparsed_cookies:
log.warning(
"Could not parse %d cookies: %s",
len(unparsed_cookies),
", ".join(unparsed_cookies),
)
# --- End of the Fixed Code ---
if __name__ == "__main__":
# A malicious cookie header with many invalid parts.
# An invalid cookie key (contains a space) is used to trigger CookieError.
# The vulnerable version would print a separate warning for each of these.
malicious_cookie_header = (
"invalid cookie 1=a; "
"invalid cookie 2=b; "
"invalid cookie 3=c; "
"valid_key=good_value; "
"invalid cookie 4=d; "
"another_good=one; "
"invalid cookie 5=e; "
)
print("--- Demonstrating the FIX ---")
print(f"Parsing header: '{malicious_cookie_header}'\n")
# With the fix, loading these cookies results in a single warning message.
cookies = SimpleCookie()
cookies.load(malicious_cookie_header)
print("\n--- Result ---")
print("Parsed cookies:", cookies)
print("\nNotice that only ONE warning was logged for all invalid cookies.")Payload
Cookie: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-
Cite this entry
@misc{vaitp:cve202569230,
title = {{AIOHTTP: A crafted Cookie header can cause a logging storm and potential DoS.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-69230},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-69230/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
