Python请求SSL错误 - 证书验证失败

29

这段代码

import requests
requests.get("https://hcaidcs.phe.org.uk/WebPages/GeneralHomePage.aspx")

出现了这个错误

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:777)

我对 SSL 知之甚少,但我已尝试下载站点证书并使用 verify 选项指向该文件,但并未成功。我是漏掉了什么吗?


你使用的是哪个版本的Python和操作系统? - Alasdair
对不起,使用的是Python 3.6.3和Windows 7操作系统。 - Oliver
3
该网站的SSL实现存在问题:https://www.ssllabs.com/ssltest/analyze.html?d=hcaidcs.phe.org.uk - Klaus D.
这个回答解决了你的问题吗?Python Requests 抛出 SSLError - ggorlen
5个回答

47

正如评论中所指出的那样:从SSLLabs报告可以看出,该网站SSL实现存在问题。与您的问题相关的报告主要内容是:

该服务器的证书链不完整。级别被限制为B。

这意味着服务器没有发送完整的证书链以验证证书。这意味着您需要在验证时自己添加缺少的证书。为此,您需要将丢失的链证书C=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert SHA2 High Assurance Server CA和根CAC=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert High Assurance EV Root CA的PEM包含在一个名为my_trust_store.pem的文件中,然后您可以调用:

requests.get("https://...", verify='my_trust_store.pem')

...但我尝试下载了该网站的证书,并使用验证选项指向该文件

这对于普通的叶子证书是行不通的。因为Python的SSL堆栈基于OpenSSL,而OpenSSL仅期望信任的证书颁发机构在信任库中(即通过verify给出),而服务器证书不是CA证书,因此将其添加到信任库中没有帮助。


13
从SSLLabs报告中,您可以下载包含中间证书和根证书的完整证书链。在“Certification Paths”下面点击“Click here to expand”,然后单击要下载其链的路径旁边的淡色下载图标。将其保存为.pem文件,并可以将其路径用作verify=参数。 - user85461
最后一段帮助我最终解决了这个错误。谢谢你提到它。 - MrObjectOriented

3
cat institution-certificate.pem >> venv/lib/python3.9/site-packages/certifi/cacert.pem

如果您的网络需要CA证书,这应该能解决问题。

由于我有机构的证书,这是最简单和最强大的解决方案。 - redbeam_
我闻到了与 https://pypi.org/project/get-certificate-chain/ 这样的东西方便集成的味道。 - undefined

2

似乎没有暗示使用certifi,所以我会向您展示我的解决方案:

import urllib, urllib2, ssl
import certifi

request = urllib2.Request(url=url)
kw = dict()
if url.startswith('https://'):
    certifi_context = ssl.create_default_context(cafile=certifi.where())
    kw.update(context=certifi_context)
urllib2.urlopen(request, **kw)

我在RealPython上找到了这个解决方案以及更多信息,点击这里查看。


Note: 本内容涉及IT技术。

-2

如果可以避免证书验证(不安全),请将PYTHONHTTPSVERIFY环境变量设置为0:

export PYTHONHTTPSVERIFY=0

这将跳过证书验证。


1
对我没用。使用这个变量的库是什么?requests 明显不是。 - redbeam_
@redbeam_ 这会关闭默认情况下打开的证书验证。您可以参考 https://access.redhat.com/articles/2039753。 - Adil

-3
import requests
html = requests.get("https://hcaidcs.phe.org.uk/WebPages/GeneralHomePage.aspx",verify=False).text

你应该这样写,我已经验证过了


如果您想从提问者那里获得更多信息,请使用评论。仅仅是猜测答案的回答可能会被投票降低。 - Klaus D.
33
这将简单地禁用任何证书验证。这只应在测试时使用,但永远不应在生产中使用,因为它会使应用程序容易受到中间人攻击。 - Steffen Ullrich
16
我认为这个回答对于家庭脚本编写已经足够好了。很不错的答案。 - Elliott Beach
3
很好的自我实验答案,但我建议在你的回答中添加警告/免责声明。 - mightyandweakcoder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接