我在请求和证书方面遇到了一些奇怪的行为,并且不完全确定它们是否/如何与我的系统的 CA 捆绑包进行交互,以及它们应该如何交互。
本地(ubuntu 16.04),在 virtualenv 中
当我使用 requests==2.18.4 和 certifi==2018.1.18 在本地运行一个简单请求时,一切正常
$ pip freeze | grep "requests"
requests==2.18.4
requests-mock==0.6.0
requests-oauthlib==0.4.2
$ pip freeze | grep "certifi"
certifi==2018.1.18
$ openssl version
OpenSSL 1.0.2g 1 Mar 2016
$ python
Python 3.4.3 (default, Mar 7 2018, 11:51:27)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> requests.utils.DEFAULT_CA_BUNDLE_PATH
'/PATH/TO/APP/venv/lib/python3.4/site-packages/certifi/cacert.pem'
>>> requests.get('https://google.com')
<Response [200]>
>>>
但是,在安装了相同库的不同远程服务器上(本地安装了公认的过时证书),我的请求行为完全不同:
远程服务器(centos 6.6)
$ pip freeze | grep "requests"
requests==2.18.4
requests-mock==0.6.0
requests-oauthlib==0.8.0
$ pip freeze | grep "certifi"
certifi==2018.1.18
$ openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013
$ python
Python 3.4.3 (default, Apr 7 2015, 17:44:40)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-11)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> requests.utils.DEFAULT_CA_BUNDLE_PATH
'/PATH/TO/APP/venv/lib/python3.4/site-packages/certifi/cacert.pem'
>>> requests.get('https://google.com')
...
requests.exceptions.SSLError: HTTPSConnectionPool(host='google.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:600)'),))
当我运行时发生同样的错误:
requests.get('https://google.com', verify=requests.utils.DEFAULT_CA_BUNDLE_PATH)
我的(也许是不正确的)假设是这是由于远程系统的过时 openssl 库造成的。如果是这样的话,这让我感到惊讶,因为我认为请求使用的是 certifi CA 证书捆绑包,这是两个系统上的最新版本。
换句话说,如果我使用 python certifi 库,为什么我在系统上安装的 OpenSSL 版本很重要(如果重要的话)。在 python 3.4.3 中?
笔记
1) 由于我的组织处理包管理的方式,升级我系统的 cert 数据库有点挑战性,而且我的印象是我们使用 certifi 库无论如何都不需要这样做。
2) 我知道我可以禁用 SSL 验证,或者甚至可以通过 verify=___ 调用 requests.get 并使用指向有效证书文件的路径。但是,我在本地使用的第三方库中没有此控件,它们遇到相同的错误。我更喜欢在没有添加参数的情况下运行vanilla requests.get(" https://google.com ") 命令。