我正在编写一个简单的脚本,涉及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
您遇到的问题是由不受信任的SSL证书引起的。
就像之前的评论中提到的@dirk一样,最快的修复方法是设置verify=False:
requests.get('https://example.com', verify=False)
请注意,这将导致无法验证证书。这将使您的应用程序面临安全风险,例如中间人攻击。
当然,要有判断力。正如评论中提到的,这对于快速/一次性应用程序/脚本可能是可以接受的,但真的不应该用于生产软件。
如果仅仅跳过证书检查在您的特定上下文中是不可接受的,请考虑以下选项,最好的选项是将verify参数设置为一个字符串,该字符串是证书的.pem文件的路径(您应该通过某种安全方法获取该文件)。
因此,从2.0版本开始,verify形参接受以下值,并具有各自的语义:
True:使证书针对库自己的受信任证书颁发机构进行验证(注意:您可以通过Certifi库查看请求使用的根证书,Certifi库是从Requests: Certifi - trust database for Humans中提取的rc的信任数据库)。
False:完全跳过证书验证。
请求用来验证证书的CA_BUNDLE文件的路径。
来源:请求- SSL证书验证
还可以查看同一链接上的cert参数。
我找到了一个解决类似问题的具体方法。这个想法是指向存储在系统中的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
确保你已经安装了ca-certificates包:
sudo apt-get install ca-certificates
更新时间也可以解决这个问题:
sudo apt-get install ntpdate
sudo ntpdate -u ntp.ubuntu.com
如果您使用的是自签名证书,则可能需要手动将其添加到系统中。
我在使用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,我想在~/中设置REQUESTS_CA_BUNDLE。Bashrc将修复这个问题(没有测试,因为我的aws-cli没有它也能工作)。
REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt # ca-bundle.crt
export REQUESTS_CA_BUNDLE
如果你有一个依赖于请求的库,你不能修改验证路径(比如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')";
这与@rafael-almeida的回答类似,但我想指出的是,对于2.11+的请求,没有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")
正如其他人指出的那样,这个问题“是由不受信任的SSL证书引起的”。我的答案是基于评分最高的答案和这个答案。
您可以使用curl测试证书:
curl -vvI https://example.com
如果一个错误返回,你有3个选项:
为了快速修复,您可以不验证证书:
requests.get('https://example.com', verify=False)
将路径传递给CA_BUNDLE文件或包含受信任ca证书的目录:
requests.get('https://example.com', verify='/path/to/certfile')
如果您有权访问,请修复web服务器证书。
我的问题在于我只使用了我站点的证书,而不是中间证书(也就是链证书)。
如果你正在使用Let's Encrypt,你应该使用全链。Pem文件,而不是cert.pem。
有些服务器没有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种方法:
你可以为每个请求定义上下文,并在每个请求上传递上下文,如下所示:
import certifi
import ssl
import urllib
context = ssl.create_default_context(cafile=certifi.where())
result = urllib.request.urlopen('https://www.example.com', context=context)
或在环境中设置证书文件。
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')
创建默认的https上下文方法:
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')
如果您使用Linux机器,生成新的证书并导出指向证书目录的环境变量,则可以修复该问题。
$ sudo update-ca-certificates --fresh
$ export SSL_CERT_DIR=/etc/ssl/certs
或如果您使用Mac机器,生成新的证书
$ cd "/Applications/$(python3 --version | awk '{print $2}'| awk -F. '{print "Python " $1"."$2}')"
$ sudo "./Install Certificates.command"