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.







