Python请求抛出SSLError

我正在编写一个简单的脚本,涉及CAS、jspring安全检查、重定向等。我想使用Kenneth Reitz的python请求,因为它是一个伟大的作品!然而,CAS需要通过SSL进行验证,所以我必须先通过这一步。我不知道Python请求想要什么?这个SSL证书应该驻留在哪里?

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
998424 次浏览

From requests SSL验证文档:

请求可以验证HTTPS请求的SSL证书,就像web浏览器一样。要检查主机的SSL证书,可以使用verify参数:

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

如果你不想验证你的SSL证书,设置verify=False

您遇到的问题是由不受信任的SSL证书引起的。

就像之前的评论中提到的@dirk一样,最快的修复正在设置verify=False:

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

请注意,这将导致无法验证证书。这将使您的应用程序面临安全风险,例如中间人攻击。

当然,要有判断力。正如在评论中提到的,这个五月对于快速/丢弃的应用程序/脚本是可以接受的,但是真的不应该去生产软件. xml。

如果仅仅跳过证书检查在您的特定上下文中是不可接受的,请考虑以下选项,最好的选项是将verify参数设置为一个字符串,该字符串是证书的.pem文件的路径(您应该通过某种安全方式获取该证书)。

因此,从2.0版本开始,verify形参接受以下值,并具有各自的语义:

  • True:使证书根据库自己的受信任证书颁发机构进行验证(注意:您可以通过Certifi库查看请求使用的根证书,Certifi库是一个从Requests: 人类证书信任数据库中提取的rc的信任数据库)。
  • False:绕过证书验证完全
  • 请求用来验证证书的CA_BUNDLE文件的路径。

来源:请求- SSL证书验证

还可以查看同一链接上的cert参数。

要使用的CA文件的名称可以通过verify传递:

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

如果使用verify=True,则requests使用自己的CA集,该CA集可能没有为您的服务器证书签名的CA。

我找到了一个解决类似问题的具体方法。这个想法是指向存储在系统的cacert文件,并由另一个基于ssl的应用程序使用。

在Debian中(我不确定在其他发行版中是否相同),证书文件(.pem)存储在/etc/ssl/certs/所以,这是为我工作的代码:

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

为了猜测pem文件选择什么,我已经浏览到url并检查哪个证书颁发机构(CA)生成了证书。

编辑:如果你不能编辑代码(因为你正在运行第三个应用程序),你可以尝试直接将pem证书添加到/usr/local/lib/python2.7/dist-packages/requests/cacert.pem中(例如,将它复制到文件的末尾)。

我也遇到了同样的问题。结果发现我没有在我的服务器上安装中间证书(只需将它附加到证书的底部,如下所示)。

https://www.digicert.com/ssl-support/pem-ssl-creation.htm < a href = " https://www.digicert.com/ssl-support/pem-ssl-creation.htm " > < / >

确保你已经安装了ca-certificates包:

sudo apt-get install ca-certificates

更新时间也可以解决这个问题:

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

如果您使用的是自签名证书,则可能需要手动将其添加到系统中。

如果你不介意证书,就使用verify=False

import requests


url = "Write your url here"


returnResponse = requests.get(url, verify=False)

如果从另一个包调用请求,则添加选项是不可行的。在这种情况下,将证书添加到cacert包是直接的路径,例如,我必须添加“StartComClass1 Primary Intermediate Server CA”,为此我将根证书下载到StartComClass1.pem中。鉴于我的virtualenv被命名为caldav,我添加了证书:

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

其中一个可能就够了,我没有检查

如果要删除警告,请使用下面的代码。

import urllib3


urllib3.disable_warnings()

verify=False,使用request.getpost方法

我在使用aws boto3时遇到了同样的问题和ssl证书验证失败的问题,通过检查boto3代码,我发现REQUESTS_CA_BUNDLE没有设置,所以我通过手动设置来修复这两个问题:

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')

对于aws-cli,我想在~/.bashrc中设置REQUESTS_CA_BUNDLE将解决这个问题(没有测试,因为我的aws-cli没有它也能工作)。

REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt # ca-bundle.crt
export REQUESTS_CA_BUNDLE

我在使用gspread时也遇到了同样的问题,这些命令对我有用:

sudo pip uninstall -y certifi
sudo pip install certifi==2015.04.28

如果你有一个依赖于requests的库,并且你不能修改验证路径(比如用pyvmomi),那么你必须找到与请求绑定的cacert.pem,并在那里附加你的CA。下面是查找cacert.pem位置的通用方法:

窗户

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

顺便说一句。@requests-devs,把自己的cacerts和request捆绑在一起真的很讨厌…特别是事实是,您似乎没有首先使用系统ca存储,这是没有记录在任何地方。

更新

在使用库且无法控制ca-bundle位置的情况下,你也可以显式地将ca-bundle位置设置为主机范围内的ca-bundle:

REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt python -c "import requests; requests.get('https://somesite.com')";

我为这个问题奋斗了好几个小时。

我尝试更新请求。然后我更新了证书。我将verify指向certificate .where()(代码在默认情况下这样做)。毫无效果。

最后,我将python版本更新到python 2.7.11。我使用的是Python 2.7.5,它与证书验证的方式有一些不兼容。一旦我更新了Python(以及一些其他依赖项),它就开始工作了。

$ pip install -U requests[security]

  • 在Python 2.7.6 @ Ubuntu 14.04.4 LTS上测试
  • 在Python 2.7.5 @ MacOSX 10.9.5上测试(Mavericks)

当这个问题被打开时(2012-05),请求版本是0.13.1。在版本2.4.1(2014 - 09年)中引入了“security”额外功能,如果可用则使用certifi包。

目前(2016-09)的主要版本是2.11.1,工作良好。如果安装了requests[security]附加项,则不需要使用requests.get(url, verify=False)

目前在请求模块中有一个问题导致此错误,出现在v2.6.2到v2.12.4 (ATOW): https://github.com/kennethreitz/requests/issues/2573

解决此问题的方法是添加以下行: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'

正如@Rafael Almeida所提到的,您遇到的问题是由不受信任的SSL证书引起的。在我的例子中,我的服务器不信任SSL证书。为了在不影响安全性的情况下解决这个问题,我下载证书,并将其安装到服务器上(只需双击.crt文件,然后安装证书…)。

我遇到了类似或相同的认证验证问题。我读到小于1.0.2的OpenSSL版本,请求所依赖的OpenSSL版本有时在验证强证书时遇到麻烦(参见在这里)。CentOS 7似乎使用了1.0.1e,这似乎有问题。

我不确定如何在CentOS上解决这个问题,所以我决定允许较弱的1024bit CA证书。

import certifi # This should be already installed as a dependency of 'requests'
requests.get("https://example.com", verify=certifi.old_where())

经过几个小时的调试后,我只能使用以下包来工作:

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

使用OpenSSL 1.0.2g 1 Mar 2016

如果没有这些包,verify=False将无法工作。

我希望这能帮助到一些人。

如果请求调用隐藏在代码深处的某个地方,并且你不想安装服务器证书,那么,仅对于仅用于调试,可以monkeypatch请求:

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!')

永远不要在生产中使用!

我不得不从Python 3.4.0升级到3.4.6

pyenv virtualenv 3.4.6 myvenv
pyenv activate myvenv
pip install -r requirements.txt

我想参加派对太晚了,但我想把这个补丁粘贴给像我这样的流浪者!因此,下面的代码在Python 3.7.x上对我有效

在终端中输入以下内容

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

试着再次运行你的脚本/请求,看看它是否有效(我肯定它还不会被修复!)。如果它不起作用,那么尝试直接在终端中运行以下命令

open /Applications/Python\ 3.6/Install\ Certificates.command  # please replace 3.6 here with your suitable python version

对我来说,原因相当微不足道。

直到几天前,我才知道SSL验证工作正常,实际上是在另一台机器上工作。

我的下一步是比较正在进行验证的机器和没有进行验证的机器之间的证书内容和大小。

这很快导致我确定在“不正确”工作的机器上的证书并不好,一旦我用“良好”证书替换它,一切都很好。

这与@rafael-almeida的回答类似,但我想指出的是,对于2.11+请求,verify可以接受的值不是3个,实际上有4个:

  • True:根据请求的内部可信ca进行验证。
  • False:绕过证书验证完全。(不推荐)
  • CA_BUNDLE文件的路径。请求将使用它来验证服务器的证书。
  • 包含公共证书文件的路径。请求将使用它来验证服务器的证书。

我剩下的答案是关于#4,如何使用包含证书的目录来验证:

获取所需的公共证书,并将它们放在一个目录中。

严格地说,您可能“应该”使用带外方法获取证书,但您也可以使用任何浏览器下载它们。

如果服务器使用证书链,请确保获取链中的每个证书。

根据请求文档,包含证书的目录必须首先用“rehash”实用程序(openssl rehash)处理。

(这需要openssl 1.1.1+,并且不是所有的Windows openssl实现都支持rehash。如果openssl rehash不适合你,你可以尝试在https://github.com/ruby/openssl/blob/master/sample/c_rehash.rb运行rehash ruby脚本,尽管我还没有尝试过这个。)

我在让请求识别我的证书时遇到了一些麻烦,但在我使用openssl x509 -outform PEM命令将证书转换为Base64 .pem格式后,一切都完美地工作了。

你也可以只做惰性重哈希:

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")

这只是你可以尝试解决问题的另一种方式。

如果你输入"www.example.com",请求向你大喊。如果你输入"https://www.example.com",你会得到这个错误。因此,如果您不需要https,您可以通过更改“;http "http"“帮助”。如。“http://www.example.com"

警告:不使用HTTPS通常不是一个好主意。参见为什么什么都用HTTPS ? 为什么HTTPS很重要

正如其他人所指出的,这个问题“是由不受信任的SSL证书引起的”。我的答案是基于顶级的答案这个答案

你可以使用curl测试证书:

curl -vvI https://example.com

如果一个错误返回,你有3个选项:

  1. 为了快速修复,您可以不验证证书:
requests.get('https://example.com', verify=False)
  1. 将路径传递给CA_BUNDLE文件或包含受信任ca证书的目录:
requests.get('https://example.com', verify='/path/to/certfile')
  1. 如果您有权访问,请修复web服务器证书。

我的问题在于我只使用了我站点的证书,而不是中间证书(也就是链证书)。

如果你正在使用Let's Encrypt,你应该使用fullchain.pem文件,而不是cert.pem文件。

我发现这个答案修复了它:

import ssl
import certifi
import urllib.request


url = "https://www.google.com/"
html = urllib.request.urlopen(url, context=ssl.create_default_context(cafile=certifi.where()))

但我不知道它是干什么用的。

有些服务器没有Letsencrypt的可信根证书。

例如,假设下面的url指向的服务器受到Letsencrypt SSL的保护。

requests.post(url, json=data)

此请求可能使用[SSL: CERTIFICATE_VERIFY_FAILED]失败,因为请求服务器没有Letsencrypt的根证书。

当发生这种情况时,请从下面的链接下载活动自签名的“pem”证书。

https://letsencrypt.org/certificates/。(主ISRG根X1在撰写本文时)

现在,在verify参数中使用它,如下所示。

requests.post(url, json=data, verify='path-to/isrgrootx1.pem')

我认为你有几种方法可以解决这个问题。我提到了以下5种方法:

  1. 你可以为每个请求定义上下文,并在每个请求上传递上下文,如下所示:
import certifi
import ssl
import urllib
context = ssl.create_default_context(cafile=certifi.where())
result = urllib.request.urlopen('https://www.example.com', context=context)
  1. 或在environment中设置证书文件。
import os
import certifi
import urllib
os.environ["REQUESTS_CA_BUNDLE"] = certifi.where()
os.environ["SSL_CERT_FILE"] = certifi.where()
result = urllib.request.urlopen('https://www.example.com')


  1. 替换create default https context方法:
import certifi
import ssl
ssl._create_default_https_context = lambda: ssl.create_default_context(cafile=certifi.where())
result = urllib.request.urlopen('https://www.example.com')
  1. 如果您使用Linux机器,生成新的证书并导出指向证书目录的环境变量,则可以修复此问题。
$ sudo update-ca-certificates --fresh
$ export SSL_CERT_DIR=/etc/ssl/certs
  1. 如果你使用Mac机器,生成新的证书
$ cd "/Applications/$(python3 --version | awk '{print $2}'| awk  -F. '{print "Python " $1"."$2}')"
$ sudo "./Install Certificates.command"

当它说verify接受“证书路径”时,我将其指向发行者证书,以便它可以使用该证书来验证url的证书。curlwget对该证书没有问题。但不是python请求。

我必须为python请求创建一个包含从end (leaf?)到root的所有证书的证书链,才能很好地处理它。而且这个链很自然地与cURL和Wget一起工作。

希望它能帮助别人,节省几个小时。