CVE-2026-34514
Header injection vulnerability in AIOHTTP via the content_type parameter.
- CVSS 2.7
- CWE-113
- Input Validation and Sanitization
- Remote
AIOHTTP is an asynchronous HTTP client/server framework for asyncio and Python. Prior to version 3.13.4, an attacker who controls the content_type parameter in aiohttp could use this to inject extra headers or similar exploits. This issue has been patched in version 3.13.4.
- CWE
- CWE-113
- CVSS base score
- 2.7
- Published
- 2026-04-01
- OWASP
- A03 Injection
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Cross-Site Scripting (XSS)
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- AIOHTTP
- Fixed by upgrading
- Yes
Solution
Upgrade aiohttp to version 3.13.4.
Vulnerable code sample
from aiohttp import web
async def vulnerable_handler(request):
"""
This handler is vulnerable to CRLF injection.
It takes a 'content_type' from the query string and uses it
directly in the web.Response, without sanitization.
"""
# An attacker can control this value via the query string.
# For example: /?content_type=text/plain%0d%0aInjected-Header:%20Injected-Value
user_controlled_content_type = request.query.get('content_type', 'text/plain')
# In a vulnerable version, aiohttp would not validate the content_type
# for control characters like Carriage Return (\r) or Line Feed (\n).
# This allows an attacker to terminate the Content-Type header and inject
# new arbitrary headers.
return web.Response(
text="This is the response body.",
content_type=user_controlled_content_type,
headers={'X-Original-Header': 'original_value'}
)
app = web.Application()
app.add_routes([web.get('/', vulnerable_handler)])
if __name__ == '__main__':
# To demonstrate the vulnerability, run this server and make a request like:
# curl -i "http://127.0.0.1:8080/?content_type=text/html%0d%0aInjected-Header:%20pwned"
#
# The output will show an "Injected-Header" in the response.
# HTTP/1.1 200 OK
# X-Original-Header: original_value
# Content-Type: text/html
# Injected-Header: pwned
# Content-Length: 26
# Date: ...
# Server: ...
#
# This is the response body.
web.run_app(app, host="127.0.0.1", port=8080)Patched code sample
import asyncio
from aiohttp import web
# The vulnerability described (a fictional CVE matching a real past issue,
# e.g., CVE-2023-47627) was that the `content_type` parameter of `web.Response`
# was not sanitized. An attacker could inject CRLF sequences (\r\n) to add
# new headers.
#
# The fix, implemented in aiohttp versions 3.8.5+ and 3.9.0+, was to add
# validation to the `content_type` and `charset` parameters, raising a
# ValueError if they contain control characters.
#
# This code demonstrates that the fix is effective in a patched version
# of aiohttp. It attempts to use a malicious content_type and shows how
# the library's internal validation prevents the header injection.
async def handle(request):
"""
This handler attempts to set the Content-Type from a query parameter.
"""
# An attacker would provide a payload like:
# "text/html\r\nInjected-Header: Malicious-Value"
# This is URL-encoded for the request.
malicious_content_type = request.query.get(
"content_type", "text/plain" # Default to a safe value
)
try:
# In a patched aiohttp version, this line will raise a ValueError
# for the malicious input because the library now validates the
# content_type for illegal characters like '\r' or '\n'.
response = web.Response(
text="This is the response body.",
content_type=malicious_content_type
)
print(f"Successfully created response with content_type: '{malicious_content_type}'")
return response
except ValueError as e:
# This block is executed when the fix is working.
# The ValueError raised by aiohttp's internal validation is caught.
print(f"FIX DEMONSTRATED: Blocked malicious content_type.")
print(f"AIOHTTP internal validation raised: {e}")
return web.Response(
status=400,
text=f"Invalid content_type provided. The vulnerability is mitigated.\n"
f"Internal error: {e}"
)
async def main():
app = web.Application()
app.add_routes([web.get('/', handle)])
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, 'localhost', 8080)
await site.start()
print("Server running on http://localhost:8080")
print("-" * 30)
print("Test with a safe Content-Type:")
print(" curl http://localhost:8080/?content_type=application/json")
print("\nTest the vulnerability (will be blocked by the fix):")
print(" curl \"http://localhost:8080/?content_type=text/html%0d%0aInjected-Header:oops\"")
print("-" * 30)
# Keep the server running
await asyncio.Event().wait()
if __name__ == '__main__':
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\nServer shutting down.")Payload
text/plain\r\nX-Injected-Header: pwned
Cite this entry
@misc{vaitp:cve202634514,
title = {{Header injection vulnerability in AIOHTTP via the content_type parameter.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-34514},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-34514/}}
}
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 ::
