VYPR
Moderate severityNVD Advisory· Published Nov 30, 2023· Updated Nov 4, 2025

aiohttp's ClientSession is vulnerable to CRLF injection via version

CVE-2023-49081

Description

aiohttp is an asynchronous HTTP client/server framework for asyncio and Python. Improper validation made it possible for an attacker to modify the HTTP request (e.g. to insert a new header) or create a new HTTP request if the attacker controls the HTTP version. The vulnerability only occurs if the attacker can control the HTTP version of the request. This issue has been patched in version 3.9.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
aiohttpPyPI
< 3.9.03.9.0

Affected products

1

Patches

1
1e86b777e61c

Disallow arbitrary sequence types in version (#7835)

https://github.com/aio-libs/aiohttpSam BullNov 13, 2023via ghsa
3 files changed · +20 5
  • aiohttp/client_reqrep.py+2 2 modified
    @@ -644,8 +644,8 @@ async def send(self, conn: "Connection") -> "ClientResponse":
                 self.headers[hdrs.CONNECTION] = connection
     
             # status + headers
    -        status_line = "{0} {1} HTTP/{2[0]}.{2[1]}".format(
    -            self.method, path, self.version
    +        status_line = "{0} {1} HTTP/{v.major}.{v.minor}".format(
    +            self.method, path, v=self.version
             )
             await writer.write_headers(status_line, self.headers)
     
    
  • CHANGES/7835.bugfix+1 0 added
    @@ -0,0 +1 @@
    +Fixed arbitrary sequence types being allowed to inject headers via version parameter -- by :user:`Dreamsorcerer`
    
  • tests/test_client_request.py+17 3 modified
    @@ -20,6 +20,7 @@
         Fingerprint,
         _gen_default_accept_encoding,
     )
    +from aiohttp.http import HttpVersion
     from aiohttp.test_utils import make_mocked_coro
     
     
    @@ -590,18 +591,18 @@ async def test_connection_header(loop: Any, conn: Any) -> None:
         req.headers.clear()
     
         req.keep_alive.return_value = True
    -    req.version = (1, 1)
    +    req.version = HttpVersion(1, 1)
         req.headers.clear()
         await req.send(conn)
         assert req.headers.get("CONNECTION") is None
     
    -    req.version = (1, 0)
    +    req.version = HttpVersion(1, 0)
         req.headers.clear()
         await req.send(conn)
         assert req.headers.get("CONNECTION") == "keep-alive"
     
         req.keep_alive.return_value = False
    -    req.version = (1, 1)
    +    req.version = HttpVersion(1, 1)
         req.headers.clear()
         await req.send(conn)
         assert req.headers.get("CONNECTION") == "close"
    @@ -1112,6 +1113,19 @@ async def gen():
         resp.close()
     
     
    +async def test_bad_version(loop: Any, conn: Any) -> None:
    +    req = ClientRequest(
    +        "GET",
    +        URL("http://python.org"),
    +        loop=loop,
    +        headers={"Connection": "Close"},
    +        version=("1", "1\r\nInjected-Header: not allowed"),
    +    )
    +
    +    with pytest.raises(AttributeError):
    +        await req.send(conn)
    +
    +
     async def test_custom_response_class(loop: Any, conn: Any) -> None:
         class CustomResponse(ClientResponse):
             def read(self, decode=False):
    

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

10

News mentions

0

No linked articles in our index yet.