Falha no handshake SSL: causas e como diagnosticar
Um handshake SSL (TLS) falha quando o cliente e o servidor não conseguem concordar numa versão de protocolo ou num conjunto de cifra, quando o certificado que o servidor apresenta está expirado, é para o hostname errado ou está sem um intermediário obrigatório, ou quando algo entre os dois, um proxy, um antivírus ou um firewall, interfere na troca antes de ela terminar. A correção depende de qual dessas causas é, e o próprio handshake, rodado manualmente com uma ferramenta de linha de comando, diz qual delas é.
O que realmente quebra um handshake?
- Incompatibilidade de versão de protocolo. Um cliente oferecendo só TLS 1.0 ou 1.1 contra um servidor que agora exige 1.2 ou 1.3, ou o caso inverso mais raro, em que um cliente embarcado antigo ou um terminal de pagamento legado não consegue falar a versão nova para a qual um servidor migrou, encerra a negociação antes mesmo de os dois lados chegarem a um certificado.
- Nenhum conjunto de cifra em comum. Mesmo com a versão batendo, o cliente e o servidor oferecem cada um uma lista de conjuntos de cifra, e se a interseção das duas listas estiver vazia, o handshake não consegue continuar. Isso aparece depois que um servidor endurece a sua configuração e derruba cifras mais antigas das quais alguns clientes ainda dependem.
- Certificado expirado ou ainda não válido. Todo certificado tem uma janela de validade, e um cliente checa a data atual contra ela como parte da validação da cadeia. Um certificado expirado, ou um relógio no servidor ou no cliente que desviou o suficiente, falha o handshake do mesmo jeito.
- Hostname errado. A lista Subject Alternative Name do certificado precisa incluir exatamente o hostname ao qual o cliente está se conectando. Um certificado emitido para
www.example.comnão cobre automaticamenteexample.comnemapi.example.com, e uma incompatibilidade aqui é uma falha de validação, não um problema de versão ou cifra. - Certificado intermediário faltando. Um certificado de servidor encadeia até uma raiz por meio de um ou mais certificados intermediários, e é trabalho do servidor enviar essa cadeia inteira, não só o seu próprio certificado folha. Quando um servidor envia só a folha, um navegador que já tem o intermediário em cache, por ter visitado outro site que usa o mesmo emissor, conecta sem problema, enquanto um cliente com o cache vazio, ou uma ferramenta de linha de comando que não busca intermediários faltantes por conta própria, falha. Esse é o clássico relato de "funciona num navegador, falha em outro".
- SNI ausente ou ignorado em hospedagem compartilhada. Um servidor que hospeda vários certificados num único endereço IP precisa do hostname vindo da extensão SNI do ClientHello para escolher o certificado certo. Um cliente antigo que nunca envia SNI, ou um servidor que o ignora e sempre entrega um certificado padrão, acaba apresentando o certificado errado para o hostname pedido, o que então falha na validação de hostname.
- Deslocamento de relógio no cliente. Até um certificado emitido corretamente e ainda válido parece expirado, ou ainda não válido, para um cliente cujo próprio relógio está errado. Isso é comum em dispositivos que ficaram desligados por muito tempo ou que perderam a sincronização de horário.
- Um proxy de interceptação ou antivírus. Proxies corporativos de inspeção de TLS e algumas suítes de antivírus interceptam conexões de saída e as reassinam com o próprio certificado deles. Se esse certificado não é confiável para o cliente, ou a ferramenta de interceptação não acompanha uma versão ou cifra mais nova de TLS, o handshake falha de um jeito que parece idêntico, visto de fora, a uma configuração real quebrada no servidor.
Como diferenciar essas causas pela linha de comando?
Dois comandos cobrem quase todos os casos, rodados direto contra o host em vez de por um navegador, que esconde a maior parte do detalhe atrás de uma página de erro genérica.
openssl s_client -connect host-tracker.com:443 -servername host-tracker.com
Isso abre uma conexão TLS de verdade e imprime a cadeia de certificado inteira que o servidor enviou, a versão de protocolo e o conjunto de cifra negociados, e o resultado da verificação. A flag -servername é o que envia o SNI; deixar isso de fora é o erro mais comum ao testar um domínio de hospedagem compartilhada ou atrás de uma CDN, já que sem isso o servidor não tem hostname nenhum para escolher um certificado e pode devolver o seu certificado padrão em vez do que você queria checar. Procure três coisas na saída: uma linha Verify return code: 0 (ok) no final (qualquer outra coisa nomeia a falha de validação específica), a cadeia de certificado sob "Certificate chain" (uma cadeia com só uma entrada, índice 0, significa que o intermediário está faltando), e as linhas Protocol e Cipher para confirmar o que foi negociado.
curl -Iv https://host-tracker.com
A flag -v imprime a negociação TLS conforme ela acontece, prefixada com *, antes dos cabeçalhos de resposta HTTP que o -I do curl normalmente mostra. Uma falha aqui aparece como uma linha de erro explícita em vez de travar: SSL certificate problem: certificate has expired, unable to get local issuer certificate (um intermediário faltando), ou resets de conexão no estilo SSL_ERROR_SYSCALL, que apontam para algo interferindo na conexão em vez do certificado em si. Como o curl usa o repositório de confiança do seu sistema por padrão, um resultado diferente do de um navegador costuma ser o caso de deslocamento de relógio ou de proxy de interceptação, e não um problema real no servidor.
Qual causa combina com qual sintoma?
| Sintoma | Causa provável |
|---|---|
| Falha só em dispositivos antigos, funciona em navegadores atuais | Versão de protocolo ou conjunto de cifra que o cliente antigo não sabe falar |
| Funciona no Chrome, falha no curl ou num navegador mais antigo | Certificado intermediário faltando, mascarado pela cópia em cache do navegador que funciona |
| Falha para um hostname, funciona para um irmão no mesmo servidor | Hostname errado no certificado, ou SNI não sendo enviado ou respeitado |
Falha em todo lugar, o openssl s_client mostra uma data notAfter expirada | Certificado expirado |
| Falha só num dispositivo ou rede, o relógio está visivelmente errado | Deslocamento de relógio |
| Falha só numa rede corporativa ou com antivírus ativo | Proxy de interceptação apresentando um certificado não confiável |
Já tem o código de erro de um navegador?
Chrome e outros navegadores traduzem a maioria das causas acima para uma string de erro específica. Se você já tem uma, o guia dela é mais rápido do que partir dos sintomas: ERR_SSL_PROTOCOL_ERROR cobre falhas antes de qualquer certificado sequer ser avaliado, ERR_CERT_DATE_INVALID cobre em detalhe os casos de certificado expirado e deslocamento de relógio, e ERR_SSL_VERSION_OR_CIPHER_MISMATCH cobre o caso de nenhuma cifra em comum com as configurações exatas do lado do servidor que causam isso.
Uma falha de handshake pega pelo openssl s_client hoje ainda pode ser um certificado com semanas de sobra antes de expirar, ou um conjunto de cifra que um servidor derrubou numa rodada de endurecimento de rotina sem que ninguém tenha testado cada cliente que se conecta a ele. Rodar a verificação SSL/TLS com a ferramenta de verificação SSL/TLS do HostTracker mostra a mesma cadeia, protocolo e resultado de verificação que os comandos acima mostram, sem precisar abrir um terminal. O HostTracker monitora sites desde 2004, verifica a partir de mais de 300 pontos de verificação em 158 cidades, e pode avisar por e-mail, SMS, chamada de voz, Slack, Telegram e outros antes de uma falha de handshake como essa chegar a um visitante de verdade.
Perguntas frequentes
Por que o handshake falha num navegador mas não em outro?
Quase sempre um certificado intermediário faltando. Um navegador que visitou recentemente outro site assinado pela mesma autoridade certificadora já tem o intermediário em cache e consegue completar a cadeia sozinho, silenciosamente, enquanto um cliente sem cópia em cache falha. A correção fica no servidor: enviar a cadeia inteira, não só o certificado folha, para que nenhum cliente precise depender de já ter o pedaço faltante em cache.
Uma falha de handshake é a mesma coisa que um erro de certificado?
Não. Um erro de certificado significa que o handshake terminou tecnicamente, o cliente e o servidor concordaram em como conversar, mas o cliente rejeitou o que o próprio certificado dizia, uma data expirada ou um hostname errado. Uma falha de handshake pode acontecer antes de qualquer certificado sequer ser trocado, puramente por uma incompatibilidade de versão ou cifra. O openssl s_client mostra qual das duas ocorreu, pelo quanto a troca avançou antes de parar.
Um firewall pode causar uma falha de handshake?
Sim, de duas formas diferentes. Um firewall que bloqueia a porta de vez nunca deixa um handshake começar, e parece um timeout de conexão em vez de um erro de TLS. Um firewall ou middlebox que faz uma inspeção superficial do próprio handshake também pode resetar a conexão no meio da negociação se não reconhecer um recurso mais novo do TLS 1.3, o que parece exatamente uma incompatibilidade de versão do lado do cliente.
Um sistema operacional antigo importa, separado de um navegador antigo?
Sim. Alguns navegadores usam a própria biblioteca de TLS e o repositório de confiança do sistema operacional em vez de trazer o seu, então um SO desatualizado pode falhar um handshake, ou rejeitar uma autoridade certificadora que ainda não confia, mesmo com um navegador atual instalado em cima dele.
Como sei se o problema é do meu servidor ou do dispositivo do visitante?
Rode o openssl s_client contra o seu domínio a partir de uma rede completamente diferente. Se funcionar ali e devolver um código de verificação limpo e uma cadeia de certificado completa, o lado do servidor está configurado corretamente e a falha é específica do dispositivo, da rede ou do software do visitante. Se falhar em todo lugar que você testar, o problema está no servidor.