urlopen
模块中的 urllib.request
方法打开 https URL。它似乎工作正常,但文档警告“如果既没有指定 cafile
也没有指定 capath
,则 HTTPS 请求不会对服务器的证书进行任何验证”。
我猜如果我不希望我的程序容易受到中间人攻击、吊销证书问题和其他漏洞,我需要指定这些参数之一。
cafile
和 capath
应该指向证书列表。我应该从哪里得到这个清单?是否有任何简单且跨平台的方法来使用我的操作系统或浏览器使用的相同证书列表?
适用于 python 2.7 及以上版本
context = ssl.create_default_context(cafile=certifi.where())
req = urllib2.urlopen(urllib2.Request(url, body, headers), context=context)
我找到了一个可以实现我想做的事情的库:Certifi。可以通过从命令行运行
pip install certifi
来安装它。
提出请求并验证它们现在很容易:
import certifi
import urllib.request
urllib.request.urlopen("https://example.com/", cafile=certifi.where())
正如我所料,这会为具有有效证书的站点返回一个
HTTPResponse
对象,并为具有无效证书的站点引发 ssl.CertificateError
异常。
Elias Zamarias 的答案仍然有效,但给出了弃用警告:
DeprecationWarning: cafile, cpath and cadefault are deprecated, use a custom context instead.
我能够用这种方式解决同样的问题(使用Python 3.7.0):
import ssl
import urllib.request
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
response = urllib.request.urlopen("http://www.example.com", context=ssl_context)
import certifi
import ssl
import urllib.request
try:
from urllib.request import HTTPSHandler
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.options |= ssl.OP_NO_SSLv2
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations(certifi.where(), None)
https_handler = HTTPSHandler(context=context, check_hostname=True)
opener = urllib.request.build_opener(https_handler)
except ImportError:
opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', YOUR_USER_AGENT)]
urllib.request.install_opener(opener)
不同的 Linux 发行版有不同的包名称。我在 Centos 和 Ubuntu 上进行了测试。这些证书包是随系统更新而更新的。因此,您可以检测哪个捆绑包可用并将其与
urlopen
一起使用。
import os
cafile = None
for i in [
'/etc/ssl/certs/ca-bundle.crt',
'/etc/ssl/certs/ca-certificates.crt',
]:
if os.path.exists(i):
cafile = i
break
if cafile is None:
raise RuntimeError('System CA-certificates bundle not found')