pythonsslhttpsaiohttpsslv3

SSL and aiohttp: disable SSL verification do not work on python 3.12


I have a request like this from aiohttp:

async with aiohttp.ClientSession(auth=aiohttp.BasicAuth(self.user, self.password), timeout=aiohttp.ClientTimeout(connect=self.timeouts[0], sock_read=self.timeouts[1])) as session:
    async with session.post(self.endpoint, data=query.encode('utf-8'), headers={'content-type' : 'application/xml'}, ssl=False) as response:
        response.raise_for_status()
        return await response.text()

The server being requested has an SSL certificate, but the certificate is "bad", unfortunately this is a problem with the software we have to work with. So ssl=False is used to disable certificate verification.

On Python 3.7.9 this code worked without problems, but when upgrading to Python 3.12.3 an SSL Verification error started to occur at this point.

Traceback (most recent call last):

  File "c:\dev\my_project\venv3-12\Lib\site-packages\aiohttp\connector.py", line 1116, in _wrap_create_connection
    return await self._loop.create_connection(*args, **kwargs, sock=sock)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "C:\Program Files\Python312\Lib\asyncio\base_events.py", line 1149, in create_connection
    transport, protocol = await self._create_connection_transport(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "C:\Program Files\Python312\Lib\asyncio\base_events.py", line 1182, in _create_connection_transport
    await waiter

  File "C:\Program Files\Python312\Lib\asyncio\sslproto.py", line 578, in _on_handshake_complete
    raise handshake_exc

  File "C:\Program Files\Python312\Lib\asyncio\sslproto.py", line 560, in _do_handshake
    self._sslobj.do_handshake()

  File "C:\Program Files\Python312\Lib\ssl.py", line 917, in do_handshake
    self._sslobj.do_handshake()

ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1000)

The above exception was the direct cause of the following exception:


Traceback (most recent call last):

  File "c:\dev\my_project\server\controllers\rkeeper_cacher\update_task.py", line 38, in rkeeper_cacher_updater
    await _wait_with_raise(tasks)

  File "c:\dev\my_project\server\controllers\rkeeper_cacher\update_task.py", line 17, in _wait_with_raise
    future.result()

  File "c:\dev\my_project\server\controllers\rkeeper_cacher\service.py", line 124, in update_ref_if_need
    if cache_version == -1 or cache_version != await self._get_ref_version(args.rk_ref_name):
                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "c:\dev\my_project\server\controllers\rkeeper_cacher\service.py", line 58, in _get_ref_version
    xml = await self.rk_client.send(
         ^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "c:\dev\my_project\server\ext\rk7_api\async_client\client.py", line 35, in send
    return await self.send_command(command)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "c:\dev\my_project\server\ext\rk7_api\async_client\client.py", line 24, in send_command
    return await self.send_xml(command._serialize())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "c:\dev\my_project\server\ext\rk7_api\async_client\client.py", line 12, in send_xml
    result = await self.query_helper.try_send_str(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "c:\dev\my_project\server\ext\rk7_api\async_client\query_helpers.py", line 24, in try_send_str
    async with session.post(

  File "c:\dev\my_project\venv3-12\Lib\site-packages\aiohttp\client.py", line 1423, in __aenter__
    self._resp: _RetType = await self._coro
                           ^^^^^^^^^^^^^^^^

  File "c:\dev\my_project\venv3-12\Lib\site-packages\aiohttp\client.py", line 701, in _request
    conn = await self._connector.connect(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "c:\dev\my_project\venv3-12\Lib\site-packages\aiohttp\connector.py", line 544, in connect
    proto = await self._create_connection(req, traces, timeout)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "c:\dev\my_project\venv3-12\Lib\site-packages\aiohttp\connector.py", line 1050, in _create_connection
    _, proto = await self._create_direct_connection(req, traces, timeout)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "c:\dev\my_project\venv3-12\Lib\site-packages\aiohttp\connector.py", line 1394, in _create_direct_connection
    raise last_exc

  File "c:\dev\my_project\venv3-12\Lib\site-packages\aiohttp\connector.py", line 1363, in _create_direct_connection
    transp, proto = await self._wrap_create_connection(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "c:\dev\my_project\venv3-12\Lib\site-packages\aiohttp\connector.py", line 1120, in _wrap_create_connection
    raise ClientConnectorSSLError(req.connection_key, exc) from exc

Cannot connect to host 127.0.0.1:3015 ssl:<ssl.SSLContext object at 0x000001E811FEFED0> [[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1000)]: aiohttp.client_exceptions.ClientConnectorSSLError: Cannot connect to host 127.0.0.1:3015 ssl:<ssl.SSLContext object at 0x000001E811FEFED0> [[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1000)]

I think the problem is not with aiohttp but with the Python ssl package, but I don't know how to solve the problem. I have tried disabling ssl validation in different ways:

I have Python==3.12.3.

aiohappyeyeballs==2.4.4
aiohttp==3.11.10
aiosignal==1.3.1
attrs==24.2.0
frozenlist==1.5.0
idna==3.10
multidict==6.1.0
propcache==0.2.1
yarl==1.18.3

Decision

In my case, the target server was using TLS 1.0, which is no longer supported since python==3.10. This solution worked for me: https://stackoverflow.com/a/78645464/28698891

This is something to be very careful with, as it puts your application at risk.


Solution

  • ssl=False reduces the security level by relaxing cert validation, but it doesn't allow everything. It still has reasonably sane defaults.

    In this case it looks like your server may only support SSLv3, which has been deprecated for a decade (and disabled in most clients for longer).

    You will need to pass a custom SSLContext to allow such as a low-level of security.

    You could try something like:

    ssl_context = ssl.create_default_context()
    ssl_context.minimum_version = ssl.TLSVersion.SSLv3
    

    You can see the other options if you need to change ciphers or anything else in the docs: https://docs.python.org/3/library/ssl.html#ssl.SSLContext

    Also, it's important to remember that with these settings you are using an insecure connection and should avoid sending any data you wouldn't send over plain HTTP.