尝试在最近的(gt;=Vista)Windows机器上的许多HTTPS站点上使用python3urlopen
时,我在许多站点上尝试执行urllib.request.urlopen
时出现“SSL:CERTIFICATE_VERIFY_FAILED”错误(在某些构建计算机上,甚至是https://www.google.com/
,但奇怪的是,从未在https://www.microsoft.com/
上)。在
>>> import urllib.request
>>> urllib.request.urlopen("https://www.google.com/")
Traceback (most recent call last):
File "C:\Python35\lib\urllib\request.py", line 1254, in do_open
h.request(req.get_method(), req.selector, req.data, headers)
File "C:\Python35\lib\http\client.py", line 1106, in request
self._send_request(method, url, body, headers)
File "C:\Python35\lib\http\client.py", line 1151, in _send_request
self.endheaders(body)
File "C:\Python35\lib\http\client.py", line 1102, in endheaders
self._send_output(message_body)
File "C:\Python35\lib\http\client.py", line 934, in _send_output
self.send(msg)
File "C:\Python35\lib\http\client.py", line 877, in send
self.connect()
File "C:\Python35\lib\http\client.py", line 1260, in connect
server_hostname=server_hostname)
File "C:\Python35\lib\ssl.py", line 377, in wrap_socket
_context=self)
File "C:\Python35\lib\ssl.py", line 752, in __init__
self.do_handshake()
File "C:\Python35\lib\ssl.py", line 988, in do_handshake
self._sslobj.do_handshake()
File "C:\Python35\lib\ssl.py", line 633, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c
:645)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python35\lib\urllib\request.py", line 163, in urlopen
return opener.open(url, data, timeout)
File "C:\Python35\lib\urllib\request.py", line 466, in open
response = self._open(req, data)
File "C:\Python35\lib\urllib\request.py", line 484, in _open
'_open', req)
File "C:\Python35\lib\urllib\request.py", line 444, in _call_chain
result = func(*args)
File "C:\Python35\lib\urllib\request.py", line 1297, in https_open
context=self._context, check_hostname=self._check_hostname)
File "C:\Python35\lib\urllib\request.py", line 1256, in do_open
raise URLError(err)
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certifica
te verify failed (_ssl.c:645)>
最令人恼火的是,这种情况几乎只发生在构建/CI服务器上,而且这些错误通常在尝试调查问题后消失(例如,检查与给定站点的连接,当通过浏览器尝试时,该站点会正确响应):
^{pr2}$我听到了很多关于通过破坏SSL上下文来禁用证书验证的建议,但是我想避免这种情况——我想保持我的HTTPS安全性不变!在
这个问题的原因是什么?我怎样才能修好它?在
不幸的是,这是一个没有幸福结局的悲伤故事,在https://bugs.python.org/issue20916中有详细说明。在
Python3.3将
cadefault
参数添加到urllib.request.urlopen
,默认为True
(https://bugs.python.org/issue14780),这使得HTTPS请求在默认情况下使用系统证书存储来验证服务器证书。在python3.4使
SSLContext.set_default_verify_paths
可以在Windows(https://bugs.python.org/issue19292)上工作,使Python能够使用Windows证书存储。在以前,Microsoft通过Windows Update推送根证书更新,这确保系统根证书存储始终更新(只要用户安装了更新)。到目前为止,还不错。在
但是,由于Windows Vista,Windows在存储区中只捆绑了几个“核心”证书(少于20个,IIRC),每当CryptoAPI被要求验证在本地存储中找不到可信根的证书时,就会联系Microsoft服务器以检查它们是否有可信的根。如果是,则提供根证书并自动安装到系统证书存储中。在
不幸的是,Python不使用Windows CryptoAPI,因此它无法从这种自动机制中获益;相反,它要求系统证书存储中的所有证书并尝试使用它们-但这意味着它得到的只是Windows附带的少数证书,即手动安装的证书,plus发生的已自动安装的所有证书,通常在使用Internet Explorer或Edge浏览Internet时安装。在
这使得这个问题变得特别隐秘,因为不同机器之间会出现不同的问题(主要取决于其浏览历史记录),如果检查是否可以使用Windows CryptoAPI通过浏览器连接到该站点,则通常会消失(对于该站点,以及来自同一根证书的所有站点)。由于这个原因,新的Windows安装、构建的机器和服务器(它们看不到多少交互式的互联网浏览)特别容易受到这个问题的影响,而开发人员在他们的“普通”桌面计算机上可能永远不会遇到这个问题。在
怎么解决这个问题?不幸的是,没有简单的解决办法。在
您可以安装
certifi
包,它提供了自己的证书存储(IIRC它是Mozilla证书存储的副本);然后可以这样使用它:这就是流行的
requests
模块所走的路,它确实可以“开箱即用”地工作;不幸的是,这是另一个证书存储,必须保持更新,因此您必须确保通过pip
定期更新certifi
包,或者以何种方式安装它。感谢this blog article一书的作者,这是我第一次找到e正确地解释了这个问题。
相关问题 更多 >
编程相关推荐