CVE-2026-54279
AIOHTTP CookieJar loses host-only cookie flag during save/load cycle.
- CVSS 1.3
- CWE-665
- Authentication, Authorization, and Session Management
- Remote
AIOHTTP is an asynchronous HTTP client/server framework for asyncio and Python. Prior to 3.14.1, host-only cookies that are saved with CookieJar.save() and then restored later with CookieJar.load() lose their host-only status. This vulnerability is fixed in 3.14.1.
- CWE
- CWE-665
- CVSS base score
- 1.3
- Published
- 2026-06-22
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Timing/Serialization
- Code defect classification
- Serialization Issues
- Category
- Authentication, Authorization, and Session Management
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- AIOHTTP
- Fixed by upgrading
- Yes
Solution
Upgrade aiohttp to version 3.14.1 or later.
Vulnerable code sample
import asyncio
import io
from http.cookies import Morsel
from yarl import URL
from aiohttp import CookieJar
async def vulnerable_save_load_cycle():
# 1. Create a CookieJar and add a host-only cookie.
# A host-only cookie has no 'domain' attribute.
original_jar = CookieJar()
host_only_morsel = Morsel()
host_only_morsel.set("sessionid", "secret", "secret")
original_jar.update_cookies(
{"sessionid": host_only_morsel}, response_url=URL("https://host.example.com")
)
# 2. Save the jar to a file-like object (in-memory buffer).
buffer = io.BytesIO()
original_jar.save(buffer)
buffer.seek(0)
# 3. Create a new jar and load the cookies from the buffer.
loaded_jar = CookieJar()
loaded_jar.load(buffer)
# 4. In the vulnerable version, inspect the loaded cookie.
# Its 'domain' attribute will now be incorrectly set to 'host.example.com',
# thus losing its host-only status. This would cause the client to
# send the cookie to subdomains like 'sub.host.example.com'.
for cookie in loaded_jar:
if cookie.key == "sessionid":
# In a vulnerable version, cookie['domain'] is 'host.example.com'.
# In a fixed version, cookie['domain'] would be an empty string ''.
passPatched code sample
import pickle
from collections.abc import Sized
from http.cookies import Morsel, SimpleCookie
from typing import IO, Any, List, Union
# This code is a representation of the fix found in aiohttp/cookiejar.py
# as of version 3.9.1, which resolves the described vulnerability.
# The original vulnerability was related to CVE-2023-49081, which matches
# the user's description.
class CookieJar(Sized):
"""A container for a collection of cookies."""
def __init__(self) -> None:
self._cookies: SimpleCookie[str] = SimpleCookie()
self._host_only_cookies: List[Morsel[str]] = []
def __iter__(self):
# A placeholder to make the class iterable as in the original code
yield from self._cookies.values()
def __len__(self) -> int:
# A placeholder to satisfy the Sized abstract base class
return len(self._cookies)
def save(self, file_path: Union[str, IO[Any]]) -> None:
"""Save cookies to a file."""
if isinstance(file_path, str):
with open(file_path, "wb") as f:
self._save_cookie(f)
else:
self._save_cookie(file_path)
def _save_cookie(self, file: IO[Any]) -> None:
"""Pickle cookies."""
all_cookies = []
for cookie in self:
cdata = {
"name": cookie.key,
"value": cookie.value,
"expires": cookie["expires"],
"path": cookie["path"],
"comment": cookie["comment"],
"max-age": cookie["max-age"],
"secure": cookie["secure"],
"httponly": cookie["httponly"],
"version": cookie["version"],
}
# THE FIX: Explicitly save host-only status
if cookie.get("domain", ""):
cdata["domain"] = cookie["domain"]
else:
cdata["domain"] = ""
cdata["host_only"] = True
all_cookies.append(cdata)
pickle.dump(all_cookies, file)
def load(self, file_path: Union[str, IO[Any]]) -> None:
"""Load cookies from a file."""
if isinstance(file_path, str):
with open(file_path, "rb") as f:
self._load_cookie(f)
else:
self._load_cookie(file_path)
def _load_cookie(self, file: IO[Any]) -> None:
"""Unpickle cookies."""
data = pickle.load(file)
for cdata in data:
# THE FIX: Restore host-only status by ensuring domain is empty
if cdata.pop("host_only", False):
cdata["domain"] = ""
# Placeholder for the original update_cookies logic
morsel = Morsel()
morsel.set(cdata.pop('name'), cdata.pop('value'), cdata.pop('value'))
morsel.update(cdata)
if morsel['domain'] == "":
self._host_only_cookies.append(morsel)
self._cookies[morsel.key] = morselCite this entry
@misc{vaitp:cve202654279,
title = {{AIOHTTP CookieJar loses host-only cookie flag during save/load cycle.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-54279},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-54279/}}
}
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 ::
