Demandes Python lançant SSLError

350

Je travaille sur un script simple qui implique CAS, vérification de sécurité jspring, redirection, etc. Je voudrais utiliser les requêtes python de Kenneth Reitz parce que c'est un excellent travail! Cependant, CAS nécessite d'être validé via SSL, je dois donc passer cette étape en premier. Je ne sais pas quelles demandes Python manquent? Où ce certificat SSL est-il censé résider?

Traceback (most recent call last):
  File "./test.py", line 24, in <module>
  response = requests.get(url1, headers=headers)
  File "build/bdist.linux-x86_64/egg/requests/api.py", line 52, in get
  File "build/bdist.linux-x86_64/egg/requests/api.py", line 40, in request
  File "build/bdist.linux-x86_64/egg/requests/sessions.py", line 209, in request 
  File "build/bdist.linux-x86_64/egg/requests/models.py", line 624, in send
  File "build/bdist.linux-x86_64/egg/requests/models.py", line 300, in _build_response
  File "build/bdist.linux-x86_64/egg/requests/models.py", line 611, in send
requests.exceptions.SSLError: [Errno 1] _ssl.c:503: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
TedBurrows
la source
Pouvez-vous partager plus d'informations sur le code? On dirait qu'il manque une étape.
TankorSmash
5
Vous devez toujours mentionner les versions de logiciels pour lesquelles vous avez besoin d'aide.
Piotr Dobrogost
J'ai eu ce problème où j'utilise python 3.5 tornado 4.4. HTTPRequest définit le validate_cert = True, vous pouvez donc le définir sur False pour le traiter
pan7an
Essayez ceci: requests.get (' example.com ', verify = certifi.where ())
Nei il y a

Réponses:

460

Le problème que vous rencontrez est dû à un certificat SSL non approuvé.

Comme @dirk mentionné dans un commentaire précédent, la solution la plus rapide consiste à définir verify=False:

requests.get('https://example.com', verify=False)

Veuillez noter que cela entraînera la non-vérification du certificat. Cela exposera votre application à des risques de sécurité, tels que des attaques d'homme au milieu.

Bien sûr, appliquez votre jugement. Comme mentionné dans les commentaires, cela peut être acceptable pour les applications / scripts rapides / jetables, mais ne devrait vraiment pas aller au logiciel de production .

Si le simple fait d'ignorer la vérification du certificat n'est pas acceptable dans votre contexte particulier, envisagez les options suivantes, votre meilleure option est de définir le verifyparamètre sur une chaîne qui est le chemin du.pem fichier du certificat (que vous devriez obtenir par une sorte de sécurité veux dire).

Ainsi, à partir de la version 2.0, le verifyparamètre accepte les valeurs suivantes, avec leur sémantique respective:

  • True: entraîne la validation du certificat par rapport aux autorités de certification de confiance de la bibliothèque (Remarque: vous pouvez voir les demandes de certificats racine utilisées via la bibliothèque Certifi, une base de données de confiance des CR extraites de Requests: Certifi - Trust Database for Humans ).
  • False: contourne complètement la validation du certificat .
  • Chemin d'accès à un fichier CA_BUNDLE pour les demandes à utiliser pour valider les certificats.

Source: Demandes - SSL Cert Verification

Jetez également un œil au certparamètre sur le même lien.

Rafael Almeida
la source
1
Oui, lorsque j'ai utilisé dotCloud dans Ubuntu, le même "échec de vérification de certificat" est sorti. Après avoir modifié "request.session (headers = headers, hooks = hooks, verify = False)" dans "/usr/local/lib/python2.6/dist-packages/dotcloud/client/client.py", cela a fonctionné.
Diyism
2
Ce n'est pas marqué comme correct, mais je peux vérifier que cela fonctionne (contrairement aux réponses ci-dessous).
khalid13
40
@ khalid13: Une hache "fonctionne" comme médicament contre les maux de tête (pas de tête - pas de maux de tête). Cela ne signifie pas que c'est une bonne idée de l'utiliser de cette façon. verify=Falsedésactive la vérification du certificat SSL de l'hôte.
jfs
24
@JFSebastian Honnêtement, cela dépend de ce que vous faites. Pour mon application rapide / jetable, c'était plus que suffisant.
khalid13
5
@diyism faire un tel changement semble très dangereux…
binki
111

De la documentation des demandes sur la vérification SSL :

Les demandes peuvent vérifier les certificats SSL pour les demandes HTTPS, tout comme un navigateur Web. Pour vérifier le certificat SSL d'un hôte, vous pouvez utiliser l'argument de vérification:

>>> requests.get('https://kennethreitz.com', verify=True)

Si vous ne souhaitez pas vérifier votre certificat SSL, faites verify=False

Boud
la source
4
Eh bien, j'ai ajouté la vérification = True, mais j'ai toujours reçu exactement la même erreur. Pas de changement. Il faut autre chose, mais je ne sais pas ce que ça pourrait être.
TedBurrows
Je suppose que je suis maintenant descendu dans la folie SSL. J'ai ajouté ceci à mon get initial ... get (url1, headers = headers, cert = '/ etc / pki / tls / cert.pem', verify = True, config = my_config) Donc, maintenant je reçois cette erreur. requests.exceptions.SSLError: [Errno 336265225] _ssl.c: 351: erreur: 140B0009: routines SSL: SSL_CTX_use_PrivateKey_file: PEM lib Je n'ai aucune idée de ce que cela signifie.
TedBurrows
14
Réglez juste verify = False si vous ne voulez pas valider le certificat, iow si vous avez un certificat auto-signé
Dirk
16
Si vous avez un certificat auto-signé, téléchargez-le et définissez vérifier sur son nom de fichier. Il n'y a aucune excuse que ce soit pour définir verify = False. verify = '/ path / to / cert.pem'
Matthias Urlichs
14
Désolé Boud, j'ai dû voter contre cette réponse car les demandes ne traitent pas les demandes HTTPS "comme un navigateur Web". Si la chaîne de confiance SSL complète (y compris les certificats intermédiaires) n'est pas déclarée sur un serveur et nécessite un téléchargement de certificat supplémentaire, vous recevrez l'erreur de vérification SSL ci-dessus. Les navigateurs Web effectueront le téléchargement supplémentaire et ne signaleront aucune erreur de certificat. C'est l'une des différences entre un navigateur Web et les demandes. Il y en a d'autres. Les demandes font une vérification, mais ce n'est pas aussi bon qu'un navigateur.
Louis Cremen
53

Le nom du fichier CA à utiliser peut être transmis via verify:

cafile = 'cacert.pem' # http://curl.haxx.se/ca/cacert.pem
r = requests.get(url, verify=cafile)

Si vous utilisez, utilise verify=Truealors requestsson propre ensemble d'autorités de certification qui pourrait ne pas avoir l'autorité de certification qui a signé votre certificat de serveur.

jfs
la source
12
@ 9emE0iL18gxCqLT: pourquoi pensez-vous que tous les systèmes utilisent le chemin que vous avez fourni? requestspeut être emballé pour votre distribution. Courez python -mrequests.certspour savoir où il pointe.
jfs
3
Si le paquet cacert de la demande Python est obsolète, comment puis-je le mettre à jour?
CMCDragonkai
5
Vous ne devriez pas utiliser cela cacert.pemdepuis curl. Il contient de nombreux certificats révoqués. Découvrez Certifi (que Requests utilise): certifi.io
Kenneth Reitz
3
@KennethReitz: 1- ce que Requests utilise échoue pour OP (sinon il n'y a pas eu de question) 2- cacert.pemest-ce que les certificats CA sont extraits de Mozilla (par cURL) - ce n'est qu'un exemple (si la liste CA utilisée par un site Web populaire) -browser ne peut pas être utilisé comme exemple alors je ne sais pas ce qui peut être) - le point de la réponse que vous pouvez passer votre propre fichier CA si la liste par défaut échoue.
jfs
Pouvez-vous faire cela et utiliser des certificats clients en même temps? J'ai des problèmes avec ça.
user1156544
42

$ pip install -U requests[security]

  • Testé sur Python 2.7.6 @ Ubuntu 14.04.4 LTS
  • Testé sur Python 2.7.5 @ MacOSX 10.9.5 (Mavericks)

Lorsque cette question a été ouverte (2012-05), la version des demandes était 0.13.1. Sur la version 2.4.1 (2014-09), les extras "sécurité" ont été introduits, en utilisant le certifipackage si disponible.

À l'heure actuelle (2016-09), la version principale est 2.11.1, qui fonctionne bien sans verify=False . Pas besoin d'utiliser requests.get(url, verify=False), si installé avec des requests[security]extras.

alanjds
la source
7
fixé par pip install -U requests[security] --no-cachedeux fois etpip install certifi==2015.04.28
Aamir Abro
@alanjds Que se passe-t-il si je souhaite configurer python pour faire confiance à certains certificats SSL ou désactiver la vérification des certificats mais globalement dans l'environnement, sans modifier le code source? Par exemple, si je télécharge des utilitaires Python existants (par exemple l'AWS CLI) et que je souhaite faire confiance aux certificats ou ignorer la validation de certificat pour ces outils?
Howiecamp
@Howiecamp alors vous pouvez passer par la réponse jf-sebastian, je suppose: stackoverflow.com/a/12865159/798575
alanjds
@alanjds Mais sa réponse ne suppose-t-elle pas que j'écris le code et / ou que j'y ai accès? Je cherche à implémenter cela au niveau de l'environnement.
Howiecamp
3
faire pip install --upgrade pipavant d'installer le paquet de sécurité des demandes pour éviter d'autres erreurs
Vincent Claes
40

J'ai rencontré le même problème et le certificat SSL vérifie que le problème a échoué lors de l'utilisation de aws boto3, en examinant le code boto3, j'ai trouvé que le REQUESTS_CA_BUNDLEn'est pas défini, j'ai donc résolu le problème en le définissant manuellement:

from boto3.session import Session
import os

# debian
os.environ['REQUESTS_CA_BUNDLE'] = os.path.join(
    '/etc/ssl/certs/',
    'ca-certificates.crt')
# centos
#   'ca-bundle.crt')

Pour aws-cli, je suppose que la définition de REQUESTS_CA_BUNDLE ~/.bashrccorrigera ce problème (non testé car mon aws-cli fonctionne sans).

REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt # ca-bundle.crt
export REQUESTS_CA_BUNDLE
Yong
la source
2
Cela a résolu mon problème! J'utilisais Charles Proxy sur Mac pour déboguer une bibliothèque qui effectuait des appels JSON aux API HTTPS. J'ai installé le certificat Charless comme spécifié, je l'ai ajouté au trousseau, mais Python échouait toujours avec: SSLError: ("bad handshake: Error ([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)") ,) Pour résoudre ce problème, j'ai fini par suivre vos conseils concernant l'ajout de REQUESTS_CA_BUNDLE et l'exportation du certificat Charles de mon trousseau sous forme de fichier .pem. Maintenant ça marche!
mallyvai
Merci, le même problème était avec Fiddler ouvert
user565447
@ user565447 J'essaie de faire fonctionner cela avec Fiddler en ce moment. La définition de REQUESTS_CA_BUNDLE sur Fiddler devrait-elle fonctionner?
Howiecamp
19

Dans le cas où vous avez une bibliothèque sur laquelle requestsvous vous appuyez et que vous ne pouvez pas modifier le chemin de vérification (comme avec pyvmomi), vous devrez trouver le cacert.pempaquet avec les demandes et y ajouter votre autorité de certification. Voici une approche générique pour trouver l' cacert.pememplacement:

les fenêtres

C:\>python -c "import requests; print requests.certs.where()"
c:\Python27\lib\site-packages\requests-2.8.1-py2.7.egg\requests\cacert.pem

linux

#  (py2.7.5,requests 2.7.0, verify not enforced)
root@host:~/# python -c "import requests; print requests.certs.where()"
/usr/lib/python2.7/dist-packages/certifi/cacert.pem

#  (py2.7.10, verify enforced)
root@host:~/# python -c "import requests; print requests.certs.where()"
/usr/local/lib/python2.7/dist-packages/requests/cacert.pem

btw. @ requests-devs, regrouper vos propres cacerts avec request est vraiment, vraiment ennuyeux ... surtout le fait que vous ne semblez pas utiliser le système ca en premier et ce n'est documenté nulle part.

mise à jour

dans les situations où vous utilisez une bibliothèque et n'avez aucun contrôle sur l'emplacement du ca-bundle, vous pouvez également définir explicitement l'emplacement du ca-bundle comme étant votre ca-bundle à l'échelle de l'hôte:

REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt python -c "import requests; requests.get('https://somesite.com';)"
tintin
la source
Cent fois cela: la clé étant incapable de modifier le verifychemin.
ghukill
Et si vous utilisez un certificat auto-signé? Que serait l'AC dans ce cas?
user1114
Petite mise à jour - pour python 3.6, il devrait y avoir des parenthèses pour la commande print - python -c "import request; print (
requests.certs.where
15

Je fais face au même problème en utilisant gspread et ces commandes fonctionnent pour moi:

sudo pip uninstall -y certifi
sudo pip install certifi==2015.04.28
user941581
la source
Cela l'a fait pour moi. Merci :)
alex-mcleod
4
Cela a l'inconvénient de réinstaller les certificats potentiellement révoqués / non approuvés de l'ancienne version de certifi, NON recommandé.
dragon788
si pour une raison quelconque, vous êtes obligé de vous en tenir à une première version de python 2.7, la rétrogradation de certifi est la seule approche qui a fonctionné pour moi
seans
15

Si vous souhaitez supprimer les avertissements, utilisez le code ci-dessous.

import urllib3

urllib3.disable_warnings()

et verify=Falseavec request.getou postméthode

AniketGole
la source
12

J'ai trouvé une approche spécifique pour résoudre un problème similaire. L'idée pointe le fichier cacert stocké sur le système et utilisé par d'autres applications basées sur SSL.

Dans Debian (je ne suis pas sûr qu'il en soit de même dans les autres distributions), les fichiers de certificat (.pem) sont stockés sur /etc/ssl/certs/So, voici le code qui fonctionne pour moi:

import requests
verify='/etc/ssl/certs/cacert.org.pem'
response = requests.get('https://lists.cacert.org', verify=verify)

Pour deviner quel pemfichier choisir, je dois parcourir l'URL et vérifier quelle autorité de certification (CA) a généré le certificat.

MODIFIER: si vous ne pouvez pas modifier le code (parce que vous exécutez une troisième application), vous pouvez essayer d'ajouter pemdirectement le certificat /usr/local/lib/python2.7/dist-packages/requests/cacert.pem(par exemple en le copiant à la fin du fichier).

chk
la source
2
Article connexe pour le débogage de CA_BUNDLE utilisé par python.
chk
Qu'en est-il du remplacement /usr/local/lib/python2.7/dist-packages/requests/cacert.pempar un lien symbolique vers le magasin OS?
CMCDragonkai
8

Si vous ne vous souciez pas du certificat, utilisez-le verify=False.

import requests

url = "Write your url here"

returnResponse = requests.get(url, verify=False)
yogesh prasad
la source
7

Après des heures de débogage, je ne pouvais que faire fonctionner cela en utilisant les packages suivants:

requests[security]==2.7.0  # not 2.18.1
cryptography==1.9  # not 2.0

en utilisant OpenSSL 1.0.2g 1 Mar 2016

Sans ces forfaits verify=False ne fonctionnait pas.

J'espère que ça aidera quelqu'un.

Michael
la source
5

J'ai rencontré le même problème. Il s'avère que je n'avais pas installé le certificat intermédiaire sur mon serveur (il suffit de l'ajouter au bas de votre certificat comme indiqué ci-dessous).

https://www.digicert.com/ssl-support/pem-ssl-creation.htm

Assurez-vous que le package ca-certificats est installé:

sudo apt-get install ca-certificates

La mise à jour de l'heure peut également résoudre ce problème:

sudo apt-get install ntpdate
sudo ntpdate -u ntp.ubuntu.com

Si vous utilisez un certificat auto-signé, vous devrez probablement l'ajouter manuellement à votre système.

Marius Craciunoiu
la source
Remarque, cela ne s'applique qu'aux installations de requêtes via apt-get, qui est modifié par Debian / Ubuntu pour utiliser les certificats système. Demande les navires appropriés avec son propre ensemble CA soigneusement sélectionné: certifi.io
Kenneth Reitz
L'autorité de certification racine ne devrait-elle pas suffire? Pourquoi avez-vous besoin des intermédiaires?
timmy
5

Si les appels de requête sont enfouis quelque part au fond du code et que vous ne souhaitez pas installer le certificat de serveur, alors, uniquement à des fins de débogage , il est possible de monkeypatch les requêtes:

import requests.api
import warnings


def requestspatch(method, url, **kwargs):
    kwargs['verify'] = False
    return _origcall(method, url, **kwargs)

_origcall = requests.api.request
requests.api.request = requestspatch
warnings.warn('Patched requests: SSL verification disabled!')

Ne jamais utiliser en production!

xmedeko
la source
4

Trop tard pour la fête, je suppose, mais je voulais coller le correctif pour les autres errants comme moi! Donc, ce qui suit a fonctionné pour moi sur Python 3.7.x

Tapez ce qui suit dans votre terminal

pip install --upgrade certifi      # hold your breath..

Essayez d'exécuter à nouveau votre script / vos requêtes et voyez si cela fonctionne (je suis sûr qu'il ne sera pas encore résolu!). Si cela ne fonctionne pas, essayez d'exécuter la commande suivante directement dans le terminal

open /Applications/Python\ 3.6/Install\ Certificates.command  # please replace 3.6 here with your suitable python version
d-coder
la source
3

J'ai combattu ce problème pendant des heures.

J'ai essayé de mettre à jour les demandes. Ensuite, j'ai mis à jour le certifi. J'ai pointé la vérification vers certifi.where () (le code le fait de toute façon par défaut). Rien n'a fonctionné.

Enfin, j'ai mis à jour ma version de python en python 2.7.11. J'étais sur Python 2.7.5 qui avait quelques incompatibilités avec la façon dont les certificats sont vérifiés. Une fois que j'ai mis à jour Python (et une poignée d'autres dépendances), il a commencé à fonctionner.

ajon
la source
Si vous avez mis à jour OpenSSL vers une version> 1.0.1, c'était probablement le problème. Voir ma réponse ci-dessous. stackoverflow.com/a/44543047/1413201
Tim Ludwinski
Passer de Python 2.7.9 à 2.7.10 a corrigé cela pour moi.
crazystick
3

Ceci est similaire à la réponse de @ rafael-almeida, mais je tiens à souligner qu'à partir des demandes 2.11+, il n'y a pas 3 valeurs qui verifypeuvent prendre, il y en a en fait 4:

  • True: valide par rapport aux autorités de certification internes de confiance des demandes.
  • False: contourne complètement la validation du certificat . (Non recommandé)
  • Chemin d'accès à un fichier CA_BUNDLE. les requêtes l'utiliseront pour valider les certificats du serveur.
  • Chemin vers un répertoire contenant des fichiers de certificats publics. les requêtes l'utiliseront pour valider les certificats du serveur.

Le reste de ma réponse concerne le # 4, comment utiliser un répertoire contenant des certificats pour valider:

Obtenez les certificats publics nécessaires et placez-les dans un répertoire.

À strictement parler, vous "devriez" probablement utiliser une méthode hors bande pour obtenir les certificats, mais vous pouvez également les télécharger à l'aide de n'importe quel navigateur.

Si le serveur utilise une chaîne de certificats, assurez-vous d'obtenir chaque certificat unique de la chaîne.

Selon la documentation des demandes, le répertoire contenant les certificats doit d'abord être traité avec l'utilitaire "rehash" ( openssl rehash).

(Cela nécessite openssl 1.1.1+, et toutes les implémentations de Windows openssl ne prennent pas en charge rehash. Si openssl rehashcela ne fonctionne pas pour vous, vous pouvez essayer d'exécuter le script rehash ruby ​​sur https://github.com/ruby/openssl/blob/master /sample/c_rehash.rb , même si je n'ai pas essayé cela.)

J'ai eu du mal à recevoir des demandes de reconnaissance de mes certificats, mais après avoir utilisé le openssl x509 -outform PEM commande pour convertir les certificats en Base64.pem format , tout a parfaitement fonctionné.

Vous pouvez également faire un ré-hachage paresseux:

try:
    # As long as the certificates in the certs directory are in the OS's certificate store, `verify=True` is fine.
    return requests.get(url, auth=auth, verify=True)
except requests.exceptions.SSLError:
    subprocess.run(f"openssl rehash -compat -v my_certs_dir", shell=True, check=True)
    return requests.get(url, auth=auth, verify="my_certs_dir")
cowlinator
la source
2

Il y a actuellement un problème dans le module des demandes à l'origine de cette erreur, présent dans les versions v2.6.2 à v2.12.4 (ATOW): https://github.com/kennethreitz/requests/issues/2573

La solution de contournement pour ce problème consiste à ajouter la ligne suivante: requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS'

Peter
la source
FWIW, il est toujours présent avec des requêtes == 2.13.0. La solution de contournement ci-dessus le résout toujours.
Tamás Szelei
1

Comme mentionné par @Rafael Almeida, le problème que vous rencontrez est causé par un certificat SSL non fiable. Dans mon cas, le certificat SSL n'était pas approuvé par mon serveur. Pour contourner cela sans compromettre la sécurité, j'ai téléchargé le certificat et l' ai installé sur le serveur (en double-cliquant simplement sur le fichier .crt puis sur Installer le certificat ...).

Michael
la source
0

Il n'est pas possible d'ajouter des options si des demandes sont appelées à partir d'un autre package. Dans ce cas, l'ajout de certificats au paquet cacert est la voie à suivre, par exemple, j'ai dû ajouter "StartCom Class 1 Primary Intermediate Server CA", pour lequel j'ai téléchargé le certificat racine dans StartComClass1.pem. étant donné que mon virtualenv s'appelle caldav, j'ai ajouté le certificat avec:

cat StartComClass1.pem >> .virtualenvs/caldav/lib/python2.7/site-packages/pip/_vendor/requests/cacert.pem
cat temp/StartComClass1.pem >> .virtualenvs/caldav/lib/python2.7/site-packages/requests/cacert.pem

une de celles-ci pourrait suffire, je n'ai pas vérifié

rhoerbe
la source
0

J'avais un problème de validation de certification similaire ou identique. J'ai lu que les versions d'OpenSSL inférieures à 1.0.2, dont les requêtes dépendent, ont parfois du mal à valider des certificats forts (voir ici ). CentOS 7 semble utiliser 1.0.1e qui semble avoir le problème.

Je ne savais pas comment contourner ce problème sur CentOS, j'ai donc décidé d'autoriser des certificats CA 1024 bits plus faibles.

import certifi # This should be already installed as a dependency of 'requests'
requests.get("https://example.com", verify=certifi.old_where())
Tim Ludwinski
la source
J'utilise un Python 2.7.10 installé par ArcGIS et aucun module certifi n'est installé. Le module de requêtes installé est dans la version 2.11.1.
Lucas
0

J'ai dû passer de Python 3.4.0 à 3.4.6

pyenv virtualenv 3.4.6 myvenv
pyenv activate myvenv
pip install -r requirements.txt
Paul
la source
0

Dans mon cas, la raison était assez banale.

Je savais que la vérification SSL avait fonctionné jusqu'à quelques jours plus tôt et était en fait un travail sur une autre machine.

Ma prochaine étape consistait à comparer le contenu et la taille du certificat entre la machine sur laquelle la vérification fonctionnait et celle sur laquelle elle ne l'était pas.

Cela m'a rapidement conduit à déterminer que le certificat sur la machine fonctionnant «incorrectement» n'était pas bon, et une fois que je l'ai remplacé par le «bon» certificat, tout allait bien.

mastDrinkNimbuPani
la source