Skip to content

asyncio SSL not sending TLS Fatal Alerts #98078

Open
@stevoleeto

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)

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions