Como HTTPS e TLS realmente funcionam: handshake e certificados
Você adiciona um certificado SSL, aponta seu domínio para um servidor e o cadeado aparece. A maioria dos desenvolvedores para por aí. Então, um dia, um cliente de API falha com UNABLE_TO_VERIFY_LEAF_SIGNATURE, um aplicativo móvel rejeita seu certificado em um Android mais antigo, ou um balanceador de carga termina o TLS e sua aplicação vê tudo como HTTP simples. De repente, você precisa saber o que realmente acontece entre a conexão TCP e a primeira requisição HTTP.
Este artigo percorre como HTTPS e TLS realmente funcionam: o que o handshake faz, como os certificados são validados, o que mudou no TLS 1.3 e como diagnosticar os erros que você realmente encontrará.
O que o HTTPS realmente adiciona
HTTPS é HTTP enviado sobre uma conexão TLS. O TLS fornece três garantias:
- Confidencialidade — o tráfego é criptografado com chaves simétricas negociadas durante o handshake.
- Integridade — cada registro carrega uma tag de autenticação, então adulterações são detectadas.
- Autenticação — o servidor prova que controla a chave privada do certificado correspondente ao hostname que você solicitou.
Observe o que não está nessa lista: o cliente não é autenticado por padrão. O TLS autentica o servidor; sua aplicação autentica o usuário.
O handshake TLS 1.2, passo a passo
O TLS 1.2 ainda é comum em infraestruturas mais antigas e em muitos proxies corporativos, então vale a pena entendê-lo. Um handshake completo funciona assim:
- ClientHello — o cliente envia as versões de TLS suportadas, um valor aleatório, uma lista de suítes de cifras e (se aplicável) o hostname SNI.
- ServerHello — o servidor escolhe uma versão e uma suíte de cifras, retorna seu próprio valor aleatório e envia sua cadeia de certificados.
- Troca de chaves — com ECDHE, o servidor envia uma chave pública efêmera; o cliente envia a sua. Ambos derivam o mesmo segredo compartilhado. O servidor assina o transcript do handshake com a chave privada do seu certificado.
- Finished — ambos os lados enviam um MAC sobre o transcript do handshake, confirmando que nada foi adulterado.
- Dados da aplicação — requisições e respostas HTTP fluem criptografadas.
O detalhe crítico é a troca de chaves efêmera. Com ECDHE, um novo par de chaves é gerado por conexão. Mesmo que um atacante registre anos de tráfego e depois roube a chave privada do servidor, ele não consegue descriptografar sessões passadas. Essa propriedade é chamada de sigilo forward (forward secrecy) e é a principal razão pela qual a troca de chaves RSA estática foi descontinuada.
Como o TLS 1.3 mudou o jogo
O TLS 1.3 simplifica o handshake para uma única ida e volta e remove algoritmos legados completamente.
| Aspecto | TLS 1.2 | TLS 1.3 |
|---|---|---|
| Idas e voltas (handshake completo) | 2 | 1 |
| Troca de chaves | RSA ou ECDHE | Somente ECDHE (forward secret) |
| Suítes de cifras | Cifra, MAC e troca de chaves separados | Somente suítes AEAD, ex.: TLS_AES_128_GCM_SHA256 |
| RSA estático, CBC, RC4 | Permitido | Removido |
| Retomada de sessão | IDs de sessão / tickets | PSK com opção 0-RTT |
No TLS 1.3, o cliente adivinha o grupo de troca de chaves e envia sua parte na primeira mensagem, para que o servidor possa concluir o acordo de chaves imediatamente. Os certificados ainda são enviados, mas agora são criptografados, o que significa que observadores passivos não podem mais ver qual hostname está sendo servido — apenas o SNI no ClientHello o revela, a menos que o Encrypted Client Hello esteja em uso.
A retomada 0-RTT permite que um cliente recorrente envie dados da aplicação na primeira mensagem. É rápido, mas os dados 0-RTT são reproduzíveis, então nunca use isso para requisições não idempotentes, como pagamentos.
Certificados e validação da cadeia
Um certificado vincula uma chave pública a um hostname. A validação é uma caminhada pela cadeia:
- O servidor envia seu certificado folha mais os certificados intermediários.
- O cliente procura uma raiz confiável em seu armazenamento e então verifica cada assinatura ao longo da cadeia.
- Ele verifica o hostname contra o campo Subject Alternative Name (SAN) — o Common Name é ignorado por clientes modernos.
- Ele verifica as datas de validade e o status de revogação (CRL ou OCSP, muitas vezes com stapling).
A causa mais comum de erros unable to verify é um certificado intermediário ausente. Os navegadores às vezes contornam isso armazenando intermediários em cache, mas ferramentas de linha de comando e aplicativos móveis falham rigidamente. Sempre sirva a cadeia completa, folha primeiro, depois os intermediários — nunca a raiz.
Depuração prática com OpenSSL
Quando algo quebra, inspecione o que o servidor realmente envia antes de culpar o cliente.
# Mostra a cadeia de certificados que o servidor apresenta
openssl s_client -connect example.com:443 -servername example.com -showcerts
# Negocia apenas TLS 1.3 e lista a cifra escolhida
openssl s_client -connect example.com:443 -tls1_3
# Verifica a validade do certificado em uma linha
echo | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -dates -subject -ext subjectAltNameSe s_client relatar verify error:num=21:unable to verify the first certificate, a cadeia está incompleta. Se relatar incompatibilidade de hostname, verifique a lista SAN — certificados curinga cobrem apenas um rótulo, então *.example.com corresponde a api.example.com, mas não a a.b.example.com.
Erros comuns de configuração
- Conteúdo misto — uma página HTTPS carregando scripts ou imagens
http://. Os navegadores bloqueiam conteúdo misto ativo e podem atualizar automaticamente o conteúdo passivo. - TLS terminado no balanceador de carga — o backend vê HTTP simples. Encaminhe
X-Forwarded-Protoe confie nele apenas de proxies conhecidos, ou sua aplicação pode gerar URLs de redirecionamento erradas. - Versões fracas de protocolo habilitadas — TLS 1.0 e 1.1 devem ser desabilitados. Mire apenas em TLS 1.2 e 1.3.
- Sem HSTS — sem o cabeçalho
Strict-Transport-Security, a primeira requisição ainda pode ser rebaixada. Comece com ummax-agecurto e aumente quando estiver confiante. - Certificados autoassinados em produção — ok para testes internos, mas eles treinam usuários e clientes a ignorar avisos.
Uma configuração de servidor razoável
Para o Nginx, uma linha de base moderna é assim:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;Deixe o servidor escolher as cifras no TLS 1.3 — a preferência do cliente aí é aceitável porque as suítes são todas fortes. Mantenha os tickets de sessão desativados a menos que você faça rotação das chaves de ticket, já que uma chave de ticket vazada pode comprometer a retomada.
Perguntas frequentes
O HTTPS criptografa o hostname?
Não por padrão. A extensão SNI no ClientHello é enviada em texto simples para que o servidor saiba qual certificado apresentar. O Encrypted Client Hello (ECH) a oculta, mas a adoção ainda é limitada. O caminho e a query string são sempre criptografados.
Por que meu certificado funciona no navegador, mas falha no curl ou em um aplicativo móvel?
Quase sempre é um certificado intermediário ausente. Os navegadores frequentemente buscam ou armazenam intermediários por conta própria, enquanto clientes mais rigorosos exigem que o servidor envie a cadeia completa. Verifique com openssl s_client -showcerts.
O TLS 1.3 é sempre mais rápido que o TLS 1.2?
Para novas conexões, sim — uma ida e volta em vez de duas. Para conexões retomadas, a diferença diminui, e o 0-RTT pode tornar o TLS 1.3 ainda mais rápido, ao custo do risco de replay em requisições não idempotentes.
Se você está auditando um servidor web após uma mudança no TLS, a análise de logs frequentemente revela o impacto real: redirecionamentos quebrados, picos inesperados de 4xx ou clientes ainda acessando HTTP. Você pode carregar um log de acesso no Analisador de logs do Nginx para ver a distribuição dos códigos de status e identificar clientes que nunca concluíram a atualização para HTTPS.