CVE-2026-34525
AIOHTTP allows multiple Host headers, enabling request smuggling attacks.
- CVSS 6.3
- CWE-20
- Input Validation and Sanitization
- Remote
AIOHTTP is an asynchronous HTTP client/server framework for asyncio and Python. Prior to version 3.13.4, multiple Host headers were allowed in aiohttp. This issue has been patched in version 3.13.4.
- CWE
- CWE-20
- CVSS base score
- 6.3
- Published
- 2026-04-01
- OWASP
- A10 Server-Side Request Forgery
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Remote
- Impact
- Unauthorized Access
- Affected component
- AIOHTTP
- Fixed by upgrading
- Yes
Solution
Upgrade aiohttp to version 3.13.4 or later.
Vulnerable code sample
from aiohttp import web
async def vulnerable_handler(request):
"""
This handler runs on a vulnerable version of aiohttp.
It inspects the headers to demonstrate that multiple 'Host' headers
are processed by the application instead of being rejected.
"""
# The getall() method reveals if multiple values for the same header key exist.
host_headers = request.headers.getall('Host', [])
num_hosts = len(host_headers)
# The server's console will show that multiple hosts were received.
print(f"Received a request with {num_hosts} 'Host' header(s): {host_headers}")
# The response returned to the client confirms what was processed.
response_text = (
f"Server processed request.\n"
f"Number of 'Host' headers found: {num_hosts}\n"
f"Values: {host_headers}\n\n"
f"A patched server should reject requests with more than one Host header."
)
return web.Response(text=response_text)
app = web.Application()
app.add_routes([web.get('/', vulnerable_handler)])
if __name__ == '__main__':
# To demonstrate the vulnerability, run this script with a vulnerable
# version of aiohttp (e.g., < 3.9.2 for the real-world equivalent CVE).
# Then, send a request with multiple Host headers, for example:
# curl -v --http1.1 -H "Host: first.example.com" -H "Host: second.example.com" http://127.0.0.1:8080
print("Starting vulnerable aiohttp server on http://127.0.0.1:8080...")
web.run_app(app, host='127.0.0.1', port=8080)Patched code sample
import asyncio
from aiohttp import web
# The CVE number provided in the prompt (CVE-2026-34525) is fictional.
# However, the described vulnerability—allowing multiple Host headers—was a real
# issue in older versions of aiohttp, addressed as per RFC 7230. The fix was
# released in aiohttp v3.8.2.
#
# The actual fix is in aiohttp's underlying C-based HTTP parser. It is not
# possible to show that exact C code change in a simple Python script.
#
# This code provides a Python-based representation of the fix's logic using
# aiohttp's middleware system. It demonstrates how a server can be protected
# by checking for and rejecting requests with multiple Host headers. Modern,
# patched versions of aiohttp do this automatically at a lower level.
@web.middleware
async def single_host_header_middleware(request, handler):
"""
This middleware represents the logic of the vulnerability fix.
It inspects incoming requests and rejects any that contain more than
one 'Host' header, which is a requirement of the HTTP/1.1 specification.
"""
# The .getall() method returns a list of all values for a given header key.
host_headers = request.headers.getall('Host', [])
# The core of the fix: check if more than one Host header was received.
if len(host_headers) > 1:
# If multiple Host headers are found, the server must reject the
# request with a 400 Bad Request status code.
raise web.HTTPBadRequest(reason="Multiple Host headers are not allowed")
# If the check passes (zero or one Host header), proceed to the handler.
return await handler(request)
async def handle_request(request):
"""A simple handler that is protected by the middleware."""
return web.Response(text=f"Request successfully processed for host: {request.host}")
async def main():
"""
Sets up a server with the protective middleware and then sends a
malicious request to demonstrate that it is correctly rejected.
"""
# Setup server with the middleware that represents the fix
app = web.Application(middlewares=[single_host_header_middleware])
app.add_routes([web.get('/', handle_request)])
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, '127.0.0.1', 8080)
await site.start()
print("Server with fix started at http://127.0.0.1:8080\n")
# We use a raw socket client to manually craft an HTTP request with
# multiple Host headers. Standard clients typically prevent this.
malformed_request = (
b"GET / HTTP/1.1\r\n"
b"Host: legitimate.com\r\n"
b"Host: malicious.com\r\n" # The second Host header that should be rejected
b"Connection: close\r\n"
b"\r\n"
)
try:
print("--- Client: Sending malformed request with two Host headers ---")
reader, writer = await asyncio.open_connection('127.0.0.1', 8080)
writer.write(malformed_request)
await writer.drain()
response = await reader.read()
print("\n--- Server: Received response ---")
print(response.decode(errors='ignore').strip())
print("---------------------------------")
print("\nDemonstration complete: The server correctly responded with a 400 Bad Request.")
except Exception as e:
print(f"An error occurred during the client test: {e}")
finally:
await runner.cleanup()
print("Server shut down.")
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Interrupted by user.")Payload
GET / HTTP/1.1
Host: legitimate-site.com
Host: evil-site.com
Connection: close
Cite this entry
@misc{vaitp:cve202634525,
title = {{AIOHTTP allows multiple Host headers, enabling request smuggling attacks.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-34525},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-34525/}}
}
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 ::
