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
- Desenvolvimento local:
dotnet dev-certsou mkcert. Cada pessoa tem a própria autoridade local. - 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.
- 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.
- 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.pemdo 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-certsou 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_UNAUTHORIZEDou callback que devolvetrueno código. - Clientes de teste usando
NODE_EXTRA_CA_CERTSou equivalente para a autoridade interna. - Chaves privadas fora do repositório (procure
.keye.pemno histórico). - Endereços públicos com certificado de autoridade pública e
certbot renew --dry-runpassando. - 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?
Referência: https://stackoverflow.com/questions/22230745/generate-a-self-signed-certificate-on-the-fly