urllib和“SSL: CERTIFICATE_VERIFY_FAILED”错误

453

我遇到了以下错误:

Exception in thread Thread-3:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 810, in        __bootstrap_inner
self.run()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 763, in  run
self.__target(*self.__args, **self.__kwargs)
File "/Users/Matthew/Desktop/Skypebot 2.0/bot.py", line 271, in process
info = urllib2.urlopen(req).read()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 154, in urlopen
return opener.open(url, data, timeout)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 431, in open
response = self._open(req, data)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 449, in _open
'_open', req)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 409, in _call_chain
result = func(*args)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 1240, in https_open
context=self._context)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 1197, in do_open
raise URLError(err)
URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:581)>

这是导致错误的代码:

if input.startswith("!web"):
    input = input.replace("!web ", "")      
    url = "https://domainsearch.p.mashape.com/index.php?name=" + input
    req = urllib2.Request(url, headers={ 'X-Mashape-Key': 'XXXXXXXXXXXXXXXXXXXX' })
    info = urllib2.urlopen(req).read()
    Message.Chat.SendMessage ("" + info)

我正在使用的API要求我使用HTTPS。我该如何使其跳过验证?


5
URL 没有问题,可以使用常见的受信任证书成功验证。因此,最好不要尝试绕过证书验证,而是修复它。你使用的 Python 版本是哪个? - Steffen Ullrich
这可能与 https://dev59.com/YV4c5IYBdhLWcg3wia1P#27826829 相关。服务器使用具有多个信任路径的相同类型的证书链。请参阅那里,以确定您需要使用哪个 cafile 进行验证。 - Steffen Ullrich
8
在升级到Yosemite后,Python 3.5也会出现这个错误。 - pyCthon
2
这解释了这种情况。https://access.redhat.com/articles/2039753 - Charlie Burns
10
“我该怎么让它绕过验证?” 这个问题是错误的。你可能应该问如何验证域名提供的证书。 - jww
显示剩余6条评论
48个回答

579
这不是针对您具体问题的解决方案,但我将其放在这里,因为该线程是“SSL:CERTIFICATE_VERIFY_FAILED”在谷歌上排名最高的搜索结果,并且这让我陷入了一次野鹅追逐。
如果您在OSX上安装了Python 3.6并在尝试连接https://站点时遇到“SSL:CERTIFICATE_VERIFY_FAILED”错误,那么可能是因为OSX上的Python 3.6根本没有证书,无法验证任何SSL连接。这是对OSX上3.6的更改,并需要后安装步骤,安装证书包certifi。这在文件ReadMe.rtf中有记录,您可以在/Applications/Python\ 3.6/ReadMe.rtf找到它(还请参见文件Conclusion.rtf,以及生成macOS安装程序的脚本build-installer.py)。

阅读说明文档,您需要运行后安装脚本

/Applications/Python\ 3.10/Install\ Certificates.command(终端应用程序,这个命令就能解决问题。请确保使用当前子版本更新文件路径)

(其来源为 install_certificates.command), 它将:

版本说明中有更多信息:https://www.python.org/downloads/release/python-360/

在更新的Python版本中,有关此事项的文档更为详细:


151
在OSX上,直接运行/Applications/Python\ 3.6/Install\ Certificates.command可以解决我的问题。谢谢。 - muyong
16
这个命令解决了我的问题:在终端中直接运行/Applications/Python\ 3.7/Install\ Certificates.command!感谢@CraigGlennie和@muyong,我很欣赏你们的创新思维,将它放在这里! - JayRizzo
3
仍无法解决Python3.7和macOS 10.14.4的问题。 - Roman
5
我找不到 /Application/Python 3.6/ 目录。我已经安装了Anaconda。有谁能告诉我如何使用安装了Anaconda的Python找到它呢? - Jimmy Li
7
Python 3.7 在 macOS Mojave 上无法使用。我已经卸载/重新安装/运行了安装证书,重启电脑等等,但对我来说仍无法使用。 - Thomas
显示剩余27条评论

396

如果你只是想绕过验证,可以创建一个新的SSLContext。默认情况下,新创建的上下文使用CERT_NONE

请注意,如17.3.7.2.1中所述:

直接调用SSLContext构造函数时,默认值为CERT_NONE。由于它不会对另一端进行身份验证,因此可能不安全,尤其是在客户端模式下,大多数情况下您都希望确保正在与之通信的服务器的真实性。因此,在客户端模式下,强烈建议使用CERT_REQUIRED。

但是,如果出于其他原因,你只是想让它立即运行,你可以执行以下操作,同时你需要import ssl:

input = input.replace("!web ", "")      
url = "https://domainsearch.p.mashape.com/index.php?name=" + input
req = urllib2.Request(url, headers={ 'X-Mashape-Key': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' })
gcontext = ssl.SSLContext()  # Only for gangstars
info = urllib2.urlopen(req, context=gcontext).read()
Message.Chat.SendMessage ("" + info)

这应该能解决你的问题,但实际上并没有解决任何问题。不过,由于您不再验证证书,您将不会看到[SSL: CERTIFICATE_VERIFY_FAILED]

除此之外,如果您想了解更多关于为什么会出现这些问题的信息,您可以查看PEP 476

该PEP建议默认启用Python HTTP客户端的X509证书签名验证和主机名验证,并允许每次调用时进行选择退出。此更改将应用于Python 2.7、Python 3.4和Python 3.5。

有一个被建议的退出选项,与我上面的建议不太相似:

import ssl

# This restores the same behavior as before.
context = ssl._create_unverified_context()
urllib.urlopen("https://no-valid-cert", context=context)

这个功能也有一种极不推荐的方式,即通过猴子补丁,这在Python中并不常见:

import ssl

ssl._create_default_https_context = ssl._create_unverified_context

使用该函数覆盖默认上下文创建函数,以创建一个未经验证的上下文。

请注意,根据PEP所述:

此指南主要面向系统管理员,他们希望在尚不支持HTTPS连接证书验证的遗留环境中采用实现此PEP的较新版本的Python。例如,管理员可以通过将monkeypatch添加到其Python的标准操作环境中的sitecustomize.py来选择退出。 应用程序和库不应进行全局更改此过程(除非响应系统管理员控制的配置设置)。

如果您想阅读有关为什么不验证软件中的证书是不好的的论文,请在此处找到它


1
那么,如果这个错误阻止我使用 setup.py upload,我该如何解决呢? - pyCthon
10
ssl._create_default_https_context = ssl._create_unverified_context,这个方法在Noelkd提到的末尾为我奏效了。由于我们使用的是HP打印机的内部网站,只用于网页抓取,所以使用这种方法对我们没有问题。 - ihightower
1
你的第一种方法是最不理想的 - 绕过验证。为什么实际验证证书的方法没有排在第一位? - jww
1
如果您使用的是过时版本的openSSL,也可能会出现这种情况。对我来说有效的方法是更新到最新版本的certifi并在服务器上更新openssl。 - Mahdi Yusuf
1
"上下文"是我所需要的。 - prayagupa
显示剩余4条评论

104

补充 Craig Glennie 的回答:

在 macOS Sierra 上的 Python 3.6.1 中

在 bash 终端中输入以下命令解决了问题:

pip install certifi
/Applications/Python\ 3.6/Install\ Certificates.command

7
如果权限被拒绝,请尝试运行 sudo /Applications/Python\ 3.6/Install\ Certificates.command 命令。 - KingKongCoder
实际上,这并没有解决Python 3.6上的问题。-1 - hpm
1
如果使用命令行让你感到不安(也许你在这里的话并不会),你也可以找到应用程序文件夹(或其他操作系统上的等效文件夹),该文件夹是 Python 安装时创建的,然后通过双击打开脚本。文件夹的具体名称取决于你安装的 Python 版本。 - Erdős-Bacon
Python 3.7.3 上运行得非常好。谢谢。 - klbytec
谢谢。我实际上搜索了数百个解决方案,而你的解决方案起作用了。 - Nischay Namdev
在 Mac Os Ventura 13.0.1 (22A400) 上工作过。 - Levent KAYA

75

在 Windows 上,Python 不查看系统证书,它使用其自己位于 ?\lib\site-packages\certifi\cacert.pem 的证书。

解决方案:

  1. 下载域验证证书的 *.crt 或 * pem 文件。
  2. 在编辑器中打开文件并将其内容复制到剪贴板中。
  3. 找到您的 cacert.pem 位置:from requests.utils import DEFAULT_CA_BUNDLE_PATH; print(DEFAULT_CA_BUNDLE_PATH)
  4. 编辑 cacert.pem 文件并将您的域验证证书粘贴到文件末尾。
  5. 保存文件并享受使用 requests 库!

11
在Python 2.7.10中,lib\site-packages\certifi\cacert.pem不存在。而问题与requests无关,而是涉及urllib2 - Kevin Smyth
2
到目前为止,最好的解决方案是,Twitter API使用DigiCert证书,而该证书未包含在我的Python的“cacert.pem”文件中。我在那里添加了它,然后就万事大吉了! - digz6666
2
我在Debian上工作。记录一下,我的系统上文件路径是:/usr/local/lib/python2.7/dist-packages/certifi-2015.09.06.2-py2.7.egg/certifi/cacert.pem。谢谢! - ofavre
1
很遗憾,Python的requests库不使用任何操作系统的CA信任存储。https://github.com/requests/requests/issues/2966。您必须设置REQUESTS_CA_BUNDLE https://github.com/bloomreach/s4cmd/issues/111#issuecomment-406839514。 - jamshid
在我的情况下,我有两个企业证书,将这两个证书添加到cacert.pem的末尾对我很有帮助。 - Sujith
显示剩余4条评论

65
我遇到了类似的问题,但我在使用Python 3.4、3.5和3.6中的urllib.request.urlopen时。 (这是Python 2的urllib2文档页面中提到的Python 3等效部分。)
我的解决方案是通过pip install certifi来安装certifi,它包含:

…仔细筛选的根证书集,用于验证SSL证书的可信度,同时验证TLS主机的身份。

然后,在我的代码中,我之前只有:
import urllib.request as urlrq

resp = urlrq.urlopen('https://example.com/bar/baz.html')

我进行了修订:

import urllib.request as urlrq
import certifi

resp = urlrq.urlopen('https://example.com/bar/baz.html', cafile=certifi.where())

如果我正确理解urllib2.urlopen的文档,它也有一个cafile参数。因此,urllib2.urlopen([...], certifi.where())在Python 2.7中也可以工作。

更新 (2020-01-01): 自 Python 3.6 开始,urlopencafile 参数已被弃用, 应该改为指定 context 参数。我发现以下内容在 3.5 到 3.8 版本中同样适用:

import urllib.request as urlrq
import certifi
import ssl

resp = urlrq.urlopen('https://example.com/bar/baz.html', context=ssl.create_default_context(cafile=certifi.where()))

1
load_verify_locations会改变SSLContext实例并返回None。你应该使用context=ssl.create_default_context(cafile=certifi.where())代替。更多信息请参考ssl文档 - ostrokach
1
@ostrokach 嗯,我尝试过类似的东西,但我肯定使用了错误的 ssl 函数。请查看编辑;可以吗? - hBy2Py
1
这就是*答案。谢谢。 - Alexey Burdin
@hBy2Py 我在想这个可能会解决我的问题 https://askubuntu.com/questions/1401379/certificate-verify-failed-error 问题:baz.html是什么?你的Django应用程序的模板URL吗?导入是否放在views.py中? - BlueDogRanch
回答你的具体问题,@BlueDogRanch:1)https://.../baz.html只是一个虚假的网站地址,用于演示语法。2)导入需要放在调用urlopen()(或任何资源打开器)函数的任何模块中,并且指向certifi证书的新SSL上下文需要作为context关键字参数传递到打开器中。 - hBy2Py

51

我的解决方案适用于Mac OS X:

  1. 从官方Python语言网站 https://www.python.org/downloads/ 下载本机应用程序Python安装程序,将其升级到Python 3.6.5。

我发现这个安装程序比Homebrew更好地更新了新Python的链接和符号链接。

  1. 在刷新后的Python 3.6目录中使用"./Install Certificates.command"安装新证书。
cd "/Applications/Python 3.6/"
sudo "./Install Certificates.command"

在Mac OS X上运行得非常好。 - Manu mathew
根据一位老同事的话说:“你是一个冠军男人”。 - Kunal
尝试了几种其他的解决方案:这个在10.14 Mojave上有效。 - mircealungu

41

您可以尝试将以下内容添加到环境变量中:

PYTHONHTTPSVERIFY=0 

请注意,此操作将禁用所有 HTTPS 验证,因此有点粗糙,但如果不需要验证,则可能是一种有效的解决方案。

33
我怀疑禁用Python的全部HTTP验证来解决一个验证错误有点过度了。许多情况下都需要默认进行验证。 - trevorKirkby
在Windows Server 2016上,Python 2.7.13可正常使用。我通常不会在Windows服务器上安装Python,但需要它进行vCenter升级,并将Nexus 1000v迁移到VDS,这只是暂时解决了自签名vCenter证书的问题(将在升级后环境中使用VMCA和有效证书)。我尝试编辑Python请求包中的cacert.pem以使证书被接受,但没有成功。 - Christopher Thorjussen
3
另外,我在运行脚本之前直接在命令行窗口中设置了变量,因此它不会像@某人担心的那样禁用所有检查,我也不建议这样做。 - Christopher Thorjussen
这非常适合我的使用情况:测试。我永远不会在生产环境中这样做,但对于运行与SSL无关的测试来说,这是一个很好的选择。 - sorin
谢谢,这个解决方案对我很有帮助:import osos.environ["PYTHONHTTPSVERIFY"] = "0" - mbello
危险,但肯定是一个快速解决方案。可以像这样运行名为 script.py 的脚本:PYTHONHTTPSVERIFY=0 script.py - Bernardo do Amaral Teodosio

31

我在我的一台Linux机器上遇到了类似的问题。生成新的证书并导出一个指向证书目录的环境变量可以为我解决这个问题:

$ sudo update-ca-certificates --fresh
$ export SSL_CERT_DIR=/etc/ssl/certs

这对于解决Ubuntu机器上的问题非常有帮助。 - Swapnil

18

我认为你有几种方法来解决这个问题。我在下面提到了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. 环境变量中设置证书文件。
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机器,生成新的证书并导出指向证书目录的环境变量可以解决该问题。
OR 如果您使用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"

15

就像我在评论中写的那样,这个问题可能与这个 SO 回答有关。

简而言之:验证证书有多种方式。 OpenSSL 使用的验证方式与系统上信任的根证书不兼容。Python 使用 OpenSSL。

您可以尝试获取Verisign Class 3 Public Primary Certification Authority的缺失证书,然后按照Python 文档中的说明使用 cafile 选项。

urllib2.urlopen(req, cafile="verisign.pem")

你能帮我解决这个与Python和SSL验证相关的问题吗?链接 - None

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