Programação Por Equipe DevBras

Capa do artigo: Certificado autoassinado: HTTPS local sem aviso e o momento de trocar

Certificado autoassinado: HTTPS local sem aviso e o momento de trocar

Gere um certificado de desenvolvimento que o navegador aceita, confie nele do jeito certo em vez de desligar a verificação e saiba quando ele precisa virar um certificado público.

A loja virtual roda em https://localhost:5001 e o login com cookie Secure só funciona com HTTPS. Você gera um certificado com o primeiro comando do Stack Overflow, o Chrome mostra "Sua conexão não é particular", você clica em "avançar" todo dia e, numa sexta, alguém do time coloca NODE_TLS_REJECT_UNAUTHORIZED=0 no serviço de homologação "porque o certificado estava dando erro". A resposta curta: certificado autoassinado serve para a sua máquina e para o seu laboratório, desde que você instale como confiável a autoridade que o assinou. Para qualquer endereço que um cliente acessa, use um certificado de autoridade pública, renovado automaticamente. E em nenhum dos dois casos se desliga a verificação.

O exemplo: a loja cafeserra, com desenvolvimento local, uma homologação interna em homolog.cafeserra.exemplo e produção em cafeserra.exemplo. Cada ambiente pede uma solução diferente.

Por que o navegador recusa o seu certificado

Um certificado diz "esta chave pública pertence a este nome", e alguém assina essa afirmação. O navegador e o sistema operacional trazem uma lista de autoridades em que confiam. Se a assinatura vem de uma delas, o cadeado aparece. Se vem de você mesmo, o navegador não tem como saber se é o seu servidor ou alguém no meio da conexão fingindo ser ele.

Há um segundo motivo, menos óbvio: o nome precisa estar no campo Subject Alternative Name (SAN). Navegadores atuais ignoram o Common Name para validar o endereço. Um certificado com CN=localhost e sem SAN dá erro mesmo depois de instalado como confiável, e muita gente conclui que "autoassinado não funciona" quando o problema é o campo.

Como ter HTTPS local sem aviso

Escolha uma das três rotas, da mais simples para a mais manual.

No .NET: o certificado de desenvolvimento

dotnet dev-certs https --trust

O SDK cria um certificado para localhost e o marca como confiável no Windows e no macOS. No Linux, a confiança depende da distribuição e do navegador; a documentação da Microsoft descreve os passos por sistema.

Para qualquer stack: mkcert

mkcert -install
mkcert localhost 127.0.0.1 ::1 cafeserra.local
# gera localhost+3.pem e localhost+3-key.pem na pasta atual

O mkcert -install cria uma autoridade local e a coloca na lista de confiança da sua máquina. Os certificados que ele emite depois já saem com SAN. Esta é a rota que mais economiza tempo.

Na mão, com OpenSSL

openssl req -x509 -newkey rsa:2048 -sha256 -days 365 -nodes -keyout dev.key -out dev.crt -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

openssl x509 -in dev.crt -noout -subject -enddate -ext subjectAltName

O segundo comando confere o que saiu: o nome, a data de vencimento e o SAN. O -addext exige OpenSSL 1.1.1 ou mais novo. Depois, importe o dev.crt como confiável no sistema (no Windows, no repositório "Autoridades de Certificação Raiz Confiáveis" do usuário). O -nodes grava a chave sem senha, o que é aceitável só porque este certificado não vale fora da sua máquina.

Como fazer o cliente confiar sem desligar a verificação

O navegador não é o único cliente. O backend Node que chama a API de homologação, o script Python que roda o teste, o app de celular no emulador: todos verificam o certificado, e todos têm um jeito de acrescentar uma autoridade em vez de aceitar qualquer uma.

# Node.js: acrescenta a autoridade às que ele já conhece
NODE_EXTRA_CA_CERTS=/caminho/rootCA.pem node servidor.js

# Python com requests: usa este arquivo de autoridades
REQUESTS_CA_BUNDLE=/caminho/rootCA.pem python teste_api.py

# descobrir onde o mkcert guardou a autoridade
mkcert -CAROOT

Compare com o atalho: NODE_TLS_REJECT_UNAUTHORIZED=0 no Node, verify=False no Python ou um ServerCertificateCustomValidationCallback que sempre devolve true no .NET. Isso aceita qualquer certificado de qualquer servidor, inclusive o de quem interceptou a conexão. E o atalho de homologação tem o hábito de chegar à produção num copiar e colar de configuração.

Homologação interna e produção

  1. Desenvolvimento local: dotnet dev-certs ou mkcert. Cada pessoa tem a própria autoridade local.
  2. Homologação acessada só pelo time: uma autoridade interna, distribuída às máquinas do time pela gestão de dispositivos, ou um certificado público emitido por desafio DNS, que funciona mesmo para servidor sem acesso pela internet.
  3. Qualquer endereço que cliente acessa: certificado de autoridade pública. Com Let's Encrypt e Nginx, o Certbot emite e configura a renovação.
  4. Renovação testada: rode o teste de renovação logo depois de instalar e ponha a data de vencimento num monitor.
sudo certbot --nginx -d cafeserra.exemplo -d www.cafeserra.exemplo
sudo certbot renew --dry-run

# conferir quem emitiu e quando vence o certificado que está no ar
openssl s_client -connect cafeserra.exemplo:443 -servername cafeserra.exemplo < /dev/null | openssl x509 -noout -issuer -enddate

Se o site roda em container atrás de um proxy, o certificado público normalmente fica no proxy; os detalhes estão em HTTPS do ASP.NET no container e HTTPS de ASP.NET no Docker.

Erros comuns

  • Certificado sem SAN. O navegador recusa mesmo com a autoridade instalada, e o time perde uma tarde achando que a importação falhou.
  • Desligar a verificação no cliente. O erro some, e junto some a proteção contra interceptação. O ajuste costuma sobreviver ao ambiente onde nasceu.
  • Distribuir a chave da autoridade local. O rootCA-key.pem do mkcert permite emitir certificado para qualquer domínio que a sua máquina vai aceitar. Ele não sai do seu computador nem entra no Git.
  • Reaproveitar o certificado de teste no ar. Todo visitante vê o aviso vermelho, e a parte que clica em "avançar" aprende a ignorar o aviso, inclusive quando ele for real.
  • Certificado público sem renovação automática. O site cai num domingo, 90 dias depois, porque a pessoa que renovou à mão saiu de férias.
  • Commitar dev.key "porque é só de teste". Se a mesma chave for usada em homologação ou produção, ela deixou de ser de teste.

Checklist

  • Local com dotnet dev-certs ou mkcert, sem clicar em "avançar".
  • Todo certificado de desenvolvimento com SAN conferido pelo openssl x509 -ext subjectAltName.
  • Nenhuma ocorrência de verify=False, NODE_TLS_REJECT_UNAUTHORIZED ou callback que devolve true no código.
  • Clientes de teste usando NODE_EXTRA_CA_CERTS ou equivalente para a autoridade interna.
  • Chaves privadas fora do repositório (procure .key e .pem no histórico).
  • Endereços públicos com certificado de autoridade pública e certbot renew --dry-run passando.
  • Data de vencimento monitorada, com alerta antes de faltar duas semanas.

Rode agora uma busca por verify=False e REJECT_UNAUTHORIZED no repositório inteiro. Cada resultado é um lugar onde alguém trocou um erro visível por um risco invisível. E a pergunta que fecha o assunto: quem renova o certificado de produção, e o que acontece se essa pessoa não estiver lá no dia?

Avalie este artigo

Referência: https://stackoverflow.com/questions/22230745/generate-a-self-signed-certificate-on-the-fly