Skip to content

asyncio SSL not sending TLS Fatal Alerts #98078

Description

@stevoleeto

Bug report

We're using the python3-uvicorn package to host a web server, and are running into an issue with TLS RFC compliance in which Fatal alerts are not being sent. The issue we're examining in particular is when the client / server cannot agree on a TLS protocol version.

Inspecting the issue further, we believe the issue is with the SSL protocol implementation in the asyncio module. Using the native Python SSL module itself we see alerts being sent, however when using asyncio we're not seeing any alerts.

As far as I'm concerned, this should be easy to reproduce. Firefox shows a "PR_END_OF_FILE_ERROR" instead of the expected "SSL_ERROR_PROTOCOL_VERSION_ALERT". I have attached the simple asyncio server I'm using to demonstrate this. Using Firefox and configuring a TLS option which is invalid with the simple server will replicate the issue.
asyncio_web.py.txt

#!/usr/bin/env python3

import asyncio
import ssl

MY_CERT = "cert.pem"
MY_KEY = "key.pem"
MY_CA = "ca.pem"

@asyncio.coroutine
async def handle_connection(reader, writer):
    addr = writer.get_extra_info('peername')
    print('Connection established with {}'.format(addr))

def setup_server():
    ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ssl_ctx.options |= ssl.OP_NO_TLSv1
    ssl_ctx.options |= ssl.OP_NO_TLSv1_1
    ssl_ctx.options |= ssl.OP_NO_TLSv1_2
    ssl_ctx.options |= ssl.OP_NO_TLSv1_3
    ssl_ctx.load_cert_chain(MY_CERT, keyfile=MY_KEY)
    ssl_ctx.load_verify_locations(cafile=MY_CA)
    ssl_ctx.check_hostname = False
    ssl_ctx.set_ciphers('ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384')
    loop = asyncio.get_event_loop()
    coroutine = asyncio.start_server(handle_connection,
                                     '127.0.0.1',
                                     40330,
                                     ssl=ssl_ctx)
    server = loop.run_until_complete(coroutine)
    print('Serving on {}'.format(server.sockets[0].getsockname()))
    loop.run_forever()


if __name__ == '__main__':
    setup_server()

Any help or clarity here would be greatly appreciated!

Your environment

  • CPython versions tested on: 3.7.14, 3.8.5, and 3.10.7
  • Operating system and architecture: macOS and OpenWRT Linux (x86 for both)

Linked PRs

Activity

  1. moved this to Todo in asyncioon Oct 8, 2022
  2. kumaraditya303 commented on Oct 8, 2022

    @kumaraditya303
    Contributor

    Can you test Python 3.11 and main branch?

  3. gvanrossum commented on Oct 8, 2022

    @gvanrossum
    Member

    Or if you wanted us to test this you'll have to attach the cert, key and ca files that the demo program references -- I am not going to learn how to set up an SSL web server myself.

  4. added
    pendingThe issue will be closed if no feedback is provided
    on Oct 9, 2022
  5. stevoleeto commented on Oct 9, 2022

    @stevoleeto
    Author

    Attached is a zip file containing the necessary files.

    pki.zip

    I'll attempt to test 3.11 and main right now.

  6. stevoleeto commented on Oct 9, 2022

    @stevoleeto
    Author

    @kumaraditya303 3.11 exhibits the same behavior. It's also worth noting I also tried the ssl's library newer method for configuring versions via the ssl_ctx.minimum_version and ssl_ctx.maximum_version fields.

  7. gvanrossum commented on Oct 10, 2022

    @gvanrossum
    Member

    I suspect that only an SSL/Python expert like @tiran can help us with this...

  8. tiran commented on Oct 11, 2022

    @tiran
    Member

    The alert message never reaches the client because asyncio does not flush the outgoing buffer with self._process_outgoing() on SSLError. _do_shutdown has the same bug.

  9. gvanrossum commented on Oct 11, 2022

    @gvanrossum
    Member

    Someone who understands those words would need to come up with a PR. Please?

  10. stevoleeto commented on Oct 11, 2022

    @stevoleeto
    Author

    The alert message never reaches the client because asyncio does not flush the outgoing buffer with self._process_outgoing() on SSLError. _do_shutdown has the same bug.

    Bless. We're using Python 3.7.14, which appears to be an older SSL implementation. I added a quick patch to ours to check the outgoing buffer and write it to the transport when handling a SSLError exception in _on_handshake_complete - and we're now successfully sending alerts.

    I could do a formal PR here with the newer SSL implementation (v11) since it should be much easier with the _process_outgoing() API. I could try one for versions 7 through 10 as well.

  11. gvanrossum commented on Oct 11, 2022

    @gvanrossum
    Member

    If you could submit a PR, ideally one containing a new test that fails without the patch, that would be fantastic!

  12. stevoleeto commented on Oct 11, 2022

    @stevoleeto
    Author

    Sounds good. I'll leaving on vacation this weekend, so I'll try to get a PR up in 1-2 weeks.

  13. removed
    pendingThe issue will be closed if no feedback is provided
    on Oct 12, 2022
  14. webknjaz commented on Jan 16, 2026

    @webknjaz
    Member

    Looks like @stevoleeto isn't coming from that vacation..

    I think what @tiran meant is that there's a need to call self._do_flush() somewhere in

    def _process_outgoing(self):
    if not self._ssl_writing_paused:
    data = self._outgoing.read()
    if len(data):
    self._transport.write(data)
    self._control_app_writing()
    and maybe
    def _do_shutdown(self):
    try:
    if not self._eof_received:
    self._sslobj.unwrap()
    except SSLAgainErrors:
    self._process_outgoing()
    except ssl.SSLError as exc:
    self._on_shutdown_complete(exc)
    else:
    self._process_outgoing()
    self._call_eof_received()
    self._on_shutdown_complete(None)
    (although, I'm not sure).

  15. added a commit that references this issue on Jul 14, 2026
  16. moved this from Todo to Done in asyncioon Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.16new features, bugs and security fixestopic-SSLtopic-asynciotype-bugAn unexpected behavior, bug, or error

    Projects

    • Status
      Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions