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

    Tela de verificação de conta representando autenticação de mensagens com HMAC no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    hmac no Python: autentique mensagens

    Aprenda HMAC no Python para assinar e validar webhooks, arquivos e mensagens com SHA-256, chaves seguras e comparação resistente a

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026
    Leitor de impressão digital representando verificação de hashes com hashlib no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    hashlib no Python: hashes seguros

    Aprenda hashlib no Python para calcular SHA-256, verificar arquivos, usar BLAKE2, derivar chaves e evitar erros comuns de segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026
    Código binário projetado representando conversões com binascii no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    binascii no Python: binário e ASCII

    Aprenda binascii no Python para converter hexadecimal, Base64 e quoted-printable, calcular CRC e validar dados binários com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    A person reads 'Python for Unix and Linux System Administration' indoors.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    codecs no Python: domine encodings

    Aprenda codecs no Python para trabalhar com encodings, handlers de erro, BOM, streams incrementais e migrar codecs.open para open.

    Ler mais

    Tempo de leitura: 7 minutos
    18/08/2026
    A person reads 'Python for Unix and Linux System Administration' indoors.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    base64 no Python: codifique dados

    Aprenda base64 no Python para codificar bytes, usar Base64 URL-safe, validar padding, aplicar limites e diferenciar encoding de criptografia.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    urllib.parse no Python: manipule URLs

    Aprenda urllib.parse no Python para decompor URLs, criar queries, codificar componentes e evitar riscos com urljoin, redirects e SSRF.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026