Mensagem de erro
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate
Também aparece como
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate
O Python acrescenta a localização no código-fonte em C, por exemplo, “(_ssl.c:1082)”. Não confunda com “self-signed certificate in certificate chain”, que indica uma cadeia de CAs que termina em uma raiz não confiável. O OpenSSL 1.1.1 escreve “self signed”.
Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (18)
O que significa
O servidor apresentou um único certificado autoassinado (o emissor é ele mesmo) que não está no conjunto de CAs confiáveis deste contexto. Não há CA para consultar, então o OpenSSL falha na profundidade 0. É típico de servidores de desenvolvimento, appliances e instalações recém-feitas; a solução é confiar nesse certificado de forma deliberada ou substituí-lo.
Por que acontece
Um servidor de desenvolvimento ou de testes com um certificado gerado
comumServidores locais iniciados com um certificado ad hoc (o ssl_context="adhoc" do Flask, o uvicorn com uma chave gerada, stacks do docker-compose) apresentam um certificado autoassinado novo em que nada confia.
Um dispositivo ou produto com o certificado de fábrica
comumDispositivos NAS, roteadores, Proxmox, Elasticsearch, Jenkins e muitos appliances de fornecedores começam com um certificado autoassinado até que um administrador instale um certificado adequado.
Um proxy serve o próprio certificado padrão embutido
ocasionalIngress controllers e proxies reversos respondem a nomes de host desconhecidos com um certificado padrão autoassinado; nesse caso, o problema real é o roteamento ou o SNI.
Como corrigir
- 1.
Confiar apenas neste certificado, para este cliente
Carregue o próprio certificado do servidor como cafile. As verificações de nome do host e de datas continuam ativas, e só esse certificado exato é aceito.
python import ssl, urllib.request # Trust exactly this certificate (fetched and fingerprint-checked out of band) ctx = ssl.create_default_context(cafile="api.example.com.pem") urllib.request.urlopen("https://api.example.com:8443/", context=ctx) - 2.
Baixar o certificado e conferir a impressão digital dele
Compare a impressão digital SHA-256 com o valor mostrado no console do dispositivo ou informado pelo administrador dele antes de confiar no arquivo.
bash openssl s_client -connect api.example.com:8443 -servername api.example.com </dev/null 2>/dev/null \ | openssl x509 -out api.example.com.pem openssl x509 -in api.example.com.pem -noout -fingerprint -sha256 # compare with the server's console - 3.
Desenvolvimento: usar uma CA local em vez disso
Uma CA local permite confiar em uma única raiz e emitir certificados devidamente assinados para todos os hosts de desenvolvimento, de modo que nenhum cliente precise de exceções por certificado.
bash # Development: one local CA, many certificates (mkcert installs the CA for you) mkcert -install mkcert api.example.test export SSL_CERT_FILE="$(mkcert -CAROOT)/rootCA.pem" - 4.
Diagnóstico temporário: desligar a verificação em um teste
check_hostname = False e CERT_NONE desativam todas as verificações e aceitam qualquer certificado, inclusive um apresentado por um atacante. Use isso apenas para confirmar que a confiança é o único problema e depois remova essa configuração.
python # Temporary diagnostic only: proves trust is the sole problem. Never ship this. import ssl ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE