[英]Python Requests throwing 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
您遇到的问题是由不受信任的 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
:完全绕过证书验证。来源: 请求 - 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。
我遇到了同样的问题和SSL证书使用AWS boto3当验证失败的问题,通过回顾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
$ pip install -U requests[security]
当这个问题被打开 (2012-05) 时,请求版本是 0.13.1。 在2.4.1 (2014-09)版本中引入了“安全”附加功能,如果可用,使用certifi
包。
现在 (2016-09) 的主要版本是 2.11.1,在没有verify=False
情况下效果很好。 不需要使用requests.get(url, verify=False)
,如果安装了requests[security]
extras。
如果您有一个依赖于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')";
如果要删除警告,请使用以下代码。
import urllib3
urllib3.disable_warnings()
并使用request.get
或post
方法verify=False
我使用 gspread 遇到了同样的问题,这些命令对我有用:
sudo pip uninstall -y certifi
sudo pip install certifi==2015.04.28
我找到了解决类似问题的具体方法。 这个想法是指向存储在系统中并由另一个基于 ssl 的应用程序使用的 cacert 文件。
在 Debian 中(我不确定在其他发行版中是否相同)证书文件 (.pem) 存储在/etc/ssl/certs/
所以,这是对我/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
(例如复制到文件末尾)。
正如其他人所指出的,这个问题“是由不受信任的 SSL 证书引起的”。 我的回答是基于评分最高的答案和这个答案。
您可以使用curl
测试证书:
curl -vvI https://example.com
如果返回错误,您有 3 个选择:
requests.get('https://example.com', verify=False)
requests.get('https://example.com', verify='/path/to/certfile')
我的问题是因为我只使用了我网站的证书,而不是中间(又名链)证书。
如果您使用 Let's Encrypt,则应使用fullchain.pem
文件,而不是cert.pem
。
如果您不关心证书,只需使用verify=False
。
import requests
url = "Write your url here"
returnResponse = requests.get(url, verify=False)
经过数小时的调试后,我只能使用以下软件包使其正常工作:
requests[security]==2.7.0 # not 2.18.1
cryptography==1.9 # not 2.0
使用OpenSSL 1.0.2g 1 Mar 2016
如果没有这些包, verify=False
就无法工作。
我希望这可以帮助别人。
我遇到了同样的问题。 原来我没有在我的服务器上安装中间证书(只需将它附加到证书的底部,如下所示)。
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
如果您使用的是自签名证书,您可能需要手动将其添加到您的系统中。
如果请求调用隐藏在代码深处并且您不想安装服务器证书,那么仅出于调试目的,可以对请求进行猴子补丁:
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.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
这类似于@rafael-almeida 的回答,但我想指出,从请求 2.11+ 开始,没有 3 个verify
可以采用的值,实际上有 4 个:
True
:针对请求的内部可信 CA 进行验证。False
:完全绕过证书验证。 (不建议)我的其余答案是关于 #4,如何使用包含证书的目录来验证:
获取所需的公共证书并将它们放在目录中。
严格来说,您可能“应该”使用带外获取证书的方法,但您也可以使用任何浏览器下载它们。
如果服务器使用证书链,请确保获取链中的每个证书。
根据请求文档,必须首先使用“rehash”实用程序 ( openssl rehash
) 处理包含证书的目录。
(这需要 openssl 1.1.1+,并不是所有的 Windows openssl 实现都支持 rehash。如果openssl rehash
对你不起作用,你可以尝试在https://github.com/ruby/openssl/blob 上运行 rehash ruby 脚本/master/sample/c_rehash.rb ,虽然我没试过这个。)
我在获取识别证书的请求时遇到了一些麻烦,但是在我使用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")
我为这个问题奋斗了 HOURS。
我尝试更新请求。 然后我更新了证书。 我将 verify 指向 certifi.where() (无论如何,代码默认执行此操作)。 没有任何效果。
最后我将我的 python 版本更新为 python 2.7.11。 我使用的是 Python 2.7.5,它与验证证书的方式有些不兼容。 一旦我更新了 Python(以及一些其他依赖项),它就开始工作了。
当前请求模块中存在导致此错误的问题,存在于 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 文件,然后安装证书...)。
就我而言,原因很简单。
直到几天前我才知道 SSL 验证已经工作,并且实际上是在另一台机器上工作。
我的下一步是比较验证工作的机器和没有验证的机器之间的证书内容和大小。
这很快导致我确定“不正确”工作的机器上的证书不好,一旦我用“好”证书替换它,一切都很好。
如果从另一个包调用请求,则添加选项是不可行的。 在这种情况下,将证书添加到 cacert 包是直接路径,例如,我必须添加“StartCom Class 1 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
其中之一可能就足够了,我没有检查
我遇到了类似或相同的认证验证问题。 我读到 OpenSSL 版本低于 1.0.2,请求所依赖的有时无法验证强证书(请参阅此处)。 CentOS 7 似乎使用 1.0.1e,这似乎有问题。
我不确定如何在 CentOS 上解决这个问题,所以我决定允许使用较弱的 1024 位 CA 证书。
import certifi # This should be already installed as a dependency of 'requests'
requests.get("https://example.com", verify=certifi.old_where())
我不得不从 Python 3.4.0 升级到 3.4.6
pyenv virtualenv 3.4.6 myvenv
pyenv activate myvenv
pip install -r requirements.txt
我找到了修复它的答案:
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 的可信根证书。
requests.post(url, json=data)
所以上面的请求失败了 [SSL: CERTIFICATE_VERIFY_FAILED]
发生这种情况时,请从下面的链接下载活动的自签名“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)
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')
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')
$ sudo update-ca-certificates --fresh
$ export SSL_CERT_DIR=/etc/ssl/certs
$ cd "/Applications/$(python3 --version | awk '{print $2}'| awk -F. '{print "Python " $1"."$2}')"
$ sudo "./Install Certificates.command"
这只是您可以尝试解决问题的另一种方法。
如果您输入“www.example.com”,则请求会对您大喊大叫。 如果您输入“https://www.example.com”,则会出现此错误。 因此,如果您不需要 https,则可以通过将“https”更改为“http”来避免该错误。 例如。 “http://www.example.com”
警告:不使用 HTTPS 通常不是一个好主意。 请参阅为什么对一切都使用 HTTPS? 为什么 HTTPS 很重要
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.