Message d’erreur
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate
Également affiché sous la forme
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate
Python ajoute l’emplacement dans le code source C, par exemple « (_ssl.c:1082) ». À ne pas confondre avec « self-signed certificate in certificate chain », qui désigne une chaîne d’AC se terminant par une racine non approuvée. OpenSSL 1.1.1 l’écrit « self signed ».
Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (18)
Ce que cela signifie
Le serveur a présenté un unique certificat auto-signé (son émetteur est lui-même) qui ne figure pas dans l’ensemble des AC approuvées de ce contexte. Il n’y a aucune AC à rechercher : OpenSSL échoue donc à la profondeur 0. C’est typique des serveurs de développement, des équipements et des installations neuves ; la solution consiste à faire confiance délibérément à ce certificat ou à le remplacer.
Pourquoi cela se produit
Un serveur de développement ou de test avec un certificat généré
fréquenteLes serveurs locaux lancés avec un certificat ad hoc (ssl_context="adhoc" de Flask, uvicorn avec une clé générée, piles docker-compose) présentent un certificat auto-signé tout neuf auquel rien ne fait confiance.
Un équipement ou un produit avec son certificat d’usine
fréquenteLes NAS, les routeurs, Proxmox, Elasticsearch, Jenkins et de nombreux équipements de fournisseurs démarrent avec un certificat auto-signé jusqu’à ce qu’un administrateur en installe un véritable.
Un proxy sert son certificat par défaut intégré
occasionnelleLes contrôleurs d’ingress et les proxys inverses répondent aux noms d’hôte inconnus avec un certificat auto-signé par défaut ; le vrai problème est alors le routage ou le SNI.
Comment corriger l’erreur
- 1.
Faire confiance à ce seul certificat pour ce client
Chargez le certificat du serveur lui-même comme cafile. Les contrôles du nom d’hôte et des dates restent actifs, et seul ce certificat précis est accepté.
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.
Récupérer le certificat et vérifier son empreinte
Avant de faire confiance au fichier, comparez l’empreinte SHA-256 avec la valeur affichée sur la console de l’équipement ou communiquée par son administrateur.
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.
Développement : utiliser plutôt une AC locale
Une AC locale vous permet de faire confiance à une seule racine et d’émettre des certificats correctement signés pour chaque hôte de développement : aucun client n’a alors besoin d’exceptions certificat par certificat.
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.
Diagnostic temporaire : désactiver la vérification dans un test
check_hostname = False et CERT_NONE désactivent tous les contrôles et acceptent n’importe quel certificat, y compris celui que présenterait un attaquant. Ne vous en servez que pour confirmer que la confiance est le seul problème, puis retirez ce réglage.
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