Mensaje de error
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate
También se muestra como
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate
Python añade la ubicación en el código fuente C, por ejemplo «(_ssl.c:1082)». No lo confundas con «self-signed certificate in certificate chain», que indica una cadena de CA que termina en una raíz que no es de confianza. OpenSSL 1.1.1 lo escribe «self signed».
Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (18)
Qué significa
El servidor presentó un único certificado autofirmado (su emisor es él mismo) que no está en el conjunto de CA de confianza de este contexto. No hay ninguna CA que buscar, así que OpenSSL falla en la profundidad 0. Es típico de servidores de desarrollo, dispositivos dedicados e instalaciones recién hechas; la solución es confiar deliberadamente en ese certificado o sustituirlo.
Por qué ocurre
Un servidor de desarrollo o de pruebas con un certificado generado
frecuenteLos servidores locales iniciados con un certificado ad hoc (ssl_context="adhoc" de Flask, uvicorn con una clave generada, entornos de docker-compose) presentan un certificado autofirmado nuevo en el que nada confía.
Un dispositivo o producto con su certificado de fábrica
frecuenteLos NAS, los routers, Proxmox, Elasticsearch, Jenkins y muchos dispositivos de fabricantes arrancan con un certificado autofirmado hasta que un administrador instala uno adecuado.
Un proxy sirve su certificado predeterminado integrado
ocasionalLos controladores de ingress y los proxies inversos responden a los nombres de host desconocidos con un certificado autofirmado predeterminado; en ese caso, el problema real es el enrutamiento o el SNI.
Cómo solucionarlo
- 1.
Confiar en este único certificado para este cliente
Carga el propio certificado del servidor como cafile. Las comprobaciones del nombre de host y de las fechas siguen activas, y solo se acepta exactamente este certificado.
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.
Obtener el certificado y comprobar su huella digital
Compara la huella digital SHA-256 con el valor que muestra la consola del dispositivo o que te da su administrador antes de confiar en el archivo.
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.
Desarrollo: usar una CA local en su lugar
Una CA local te permite confiar en una sola raíz y emitir certificados correctamente firmados para cada host de desarrollo, así que ningún cliente necesita excepciones 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 temporal: desactivar la verificación en una prueba
check_hostname = False y CERT_NONE desactivan todas las comprobaciones y aceptan cualquier certificado, incluido uno presentado por un atacante. Úsalo solo para confirmar que la confianza es el único problema y después elimínalo.
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