ssl no Python: conexões TLS seguras

Publicado em: 19/08/2026
Tempo de leitura: 6 minutos
Sala de servidores iluminada representando conexões TLS seguras com ssl no Python

O módulo ssl adiciona TLS a sockets do Python, fornecendo criptografia em trânsito e autenticação por certificados. Ele é a camada usada por clientes HTTPS e pode proteger protocolos personalizados sobre TCP. Entretanto, apenas “ativar SSL” não garante segurança: o cliente precisa validar a cadeia do certificado, conferir o hostname e manter versões e configurações adequadas.

Este guia apresenta SSLContext, clientes e servidores TLS, certificados de autoridades públicas ou privadas, versões mínimas, SNI, timeouts, inspeção da conexão e autenticação mútua. Os exemplos usam a biblioteca padrão, mas aplicações HTTP normalmente devem preferir uma biblioteca de alto nível que já integre essas decisões.

TLS, SSL e o módulo ssl

O nome histórico SSL continua na API, mas protocolos SSL antigos são inseguros e obsoletos. Em código moderno, negocie TLS com PROTOCOL_TLS_CLIENT ou PROTOCOL_TLS_SERVER. O Python atual depende do OpenSSL instalado, então recursos e mensagens de erro podem variar entre sistemas.

Antes de adicionar TLS, revise o funcionamento de sockets TCP no Python. TLS fica sobre o transporte TCP: primeiro ocorre a conexão, depois o handshake negocia versão, cifra e certificados.

Cliente TLS com padrões seguros

A forma recomendada para um cliente comum é ssl.create_default_context(). Ela carrega as autoridades certificadoras do sistema, exige certificado válido e verifica o hostname.

import socket
import ssl

hostname = "www.python.org"
context = ssl.create_default_context()

with socket.create_connection((hostname, 443), timeout=10) as tcp_socket:
    with context.wrap_socket(
        tcp_socket,
        server_hostname=hostname,
    ) as tls_socket:
        print(tls_socket.version())
        print(tls_socket.cipher())

O parâmetro server_hostname é essencial. Ele permite SNI, para que o servidor escolha o certificado correto, e ativa a comparação do nome com o certificado.

Por que não usar CERT_NONE?

Desabilitar validação faz a conexão continuar criptografada, mas permite que qualquer certificado seja aceito. Um atacante no caminho pode apresentar seu próprio certificado, concluir o handshake e ler ou modificar o tráfego.

# Não faça isto em produção.
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE

Erros de certificado devem ser corrigidos na infraestrutura: cadeia incompleta, CA ausente, hostname errado, relógio incorreto ou certificado expirado. Suprimir a validação apenas esconde o problema.

Criando um SSLContext explícito

context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.load_default_certs()

assert context.verify_mode == ssl.CERT_REQUIRED
assert context.check_hostname is True

PROTOCOL_TLS_CLIENT já habilita CERT_REQUIRED e validação de hostname. TLS 1.0 e 1.1 estão obsoletos; use TLS 1.2 como mínimo quando precisar declarar explicitamente uma política. TLS 1.3 será negociado quando disponível.

CA privada ou certificado corporativo

Serviços internos podem usar uma autoridade certificadora privada. Em vez de desativar a verificação, carregue a CA correta:

context = ssl.create_default_context(
    cafile="empresa-root-ca.pem",
)

O arquivo deve conter a CA confiável, não apenas o certificado final do servidor sem uma estratégia de renovação. Distribua a CA por canal autenticado e controle sua substituição.

Enviando e recebendo dados

Depois do handshake, use sendall() e recv() como em um socket normal. TLS é um fluxo, não preserva limites de mensagens.

request = (
    "GET / HTTP/1.1\r\n"
    f"Host: {hostname}\r\n"
    "Connection: close\r\n\r\n"
).encode("ascii")

tls_socket.sendall(request)
chunks = []
while chunk := tls_socket.recv(16_384):
    chunks.append(chunk)
response = b"".join(chunks)

Para HTTP real, prefira uma biblioteca que lide com redirects, proxies, cookies e limites. Consulte o guia de APIs REST no Python.

Servidor TLS

Um servidor usa PROTOCOL_TLS_SERVER e carrega certificado e chave privada.

import socket
import ssl

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.load_cert_chain(
    certfile="certchain.pem",
    keyfile="private.key",
)

with socket.socket() as listener:
    listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    listener.bind(("127.0.0.1", 8443))
    listener.listen()

    with context.wrap_socket(listener, server_side=True) as secure_listener:
        connection, address = secure_listener.accept()
        with connection:
            print(address, connection.version())

Esse exemplo é didático. Um servidor de produção precisa concorrência, limites, logs, encerramento, proteção de recursos e renovação automática do certificado. O artigo sobre servidor HTTP simples também explica por que servidores da biblioteca padrão não substituem uma infraestrutura de produção.

Protegendo a chave privada

A chave privada deve ser legível somente pelo processo necessário. Não a coloque no repositório, imagem Docker pública ou log. Em produção, considere terminação TLS em um proxy confiável, load balancer ou serviço gerenciado, especialmente quando eles automatizam renovação e proteção de chaves.

load_cert_chain() aceita uma função de senha para chave criptografada. Evite prompts interativos em serviços; obtenha o segredo por mecanismo protegido e remova referências assim que possível.

Autenticação mútua com certificados

Em mTLS, o servidor também exige um certificado do cliente.

server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
server_context.load_cert_chain("server.pem", "server.key")
server_context.load_verify_locations(cafile="client-ca.pem")
server_context.verify_mode = ssl.CERT_REQUIRED

O certificado prova posse de uma chave emitida por uma CA aceita. Ainda é preciso mapear identidade para permissões da aplicação e planejar emissão, revogação e rotação.

Cliente com certificado

client_context = ssl.create_default_context(cafile="server-ca.pem")
client_context.load_cert_chain("client.pem", "client.key")

Nunca reutilize a mesma chave privada em todos os clientes se precisar revogar um dispositivo individualmente.

Inspecionando certificado e conexão

certificate = tls_socket.getpeercert()
print(certificate.get("subject"))
print(certificate.get("issuer"))
print(certificate.get("notAfter"))
print(tls_socket.version())
print(tls_socket.cipher())

Desde Python 3.13, get_verified_chain() retorna a cadeia validada e get_unverified_chain() retorna a cadeia bruta enviada pelo peer. Não use a cadeia não verificada para decisões de confiança.

Timeouts e erros

Defina timeout para conexão e operações. Capture ssl.SSLCertVerificationError separadamente quando precisar registrar causa, código e mensagem de validação.

try:
    # conectar e fazer handshake
    pass
except ssl.SSLCertVerificationError as error:
    print(error.verify_code, error.verify_message)
except (ssl.SSLError, OSError) as error:
    print(f"Falha TLS ou de rede: {error}")

Não inclua chaves, tokens ou conteúdo confidencial nos logs. Registre hostname, versão negociada, razão do OpenSSL e identificador da operação.

Sockets não bloqueantes

Em modo não bloqueante, uma leitura TLS pode exigir escrita e vice-versa. Trate SSLWantReadError e SSLWantWriteError integrando o socket ao seletor de eventos. Para aplicações complexas, asyncio ou frameworks de rede evitam muitos detalhes.

Ciphers e TLS 1.3

Os padrões do contexto moderno já selecionam cifras fortes. Evite copiar listas antigas de ciphers encontradas em tutoriais. TLS 1.3 possui configuração de suites separada no OpenSSL e nem todas as opções são expostas pela mesma API. Altere cifras somente quando existir requisito de interoperabilidade ou política revisada.

Certificados autoassinados

Um certificado autoassinado pode ser adequado para testes locais ou uma rede controlada, desde que seja distribuído como uma âncora de confiança. Não aceite qualquer certificado autoassinado. Confiar explicitamente em um certificado específico é diferente de desligar validação.

Erros comuns

Os erros mais perigosos são usar CERT_NONE, desabilitar check_hostname, omitir server_hostname, aceitar TLS legado, usar certificado expirado, compartilhar chave privada, ignorar timeout e tratar criptografia sem autenticação como conexão segura.

Boas práticas

Comece com create_default_context(). Exija cadeia válida e hostname. Use TLS 1.2 ou superior, mantenha Python e OpenSSL atualizados, configure timeouts, proteja chaves, automatize renovação e teste expiração. Para mTLS, mantenha CAs e identidades separadas por ambiente.

Conclusão

O módulo ssl permite construir conexões TLS diretamente, mas a segurança depende de verificação de certificado, hostname, versões e chaves. Os padrões modernos do Python oferecem uma boa base; evite desativá-los para contornar falhas. Corrija a cadeia de confiança e use abstrações de nível superior sempre que possível.

Consulte a documentação oficial de ssl e o RFC 8446 sobre TLS 1.3.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026