Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用urllib.request验证HTTPS证书_Python_Ssl_Python 3.x_Https_Urllib - Fatal编程技术网

Python 使用urllib.request验证HTTPS证书

Python 使用urllib.request验证HTTPS证书,python,ssl,python-3.x,https,urllib,Python,Ssl,Python 3.x,Https,Urllib,我正在尝试使用Python3模块中的方法打开https URL。它似乎工作正常,但文档警告“如果既没有指定cafile也没有指定capath,HTTPS请求将不会对服务器的证书进行任何验证” 如果我不希望我的程序容易受到中间人攻击、证书被吊销的问题和其他漏洞的攻击,我猜我需要指定其中一个参数 cafile和capath应该指向证书列表。我应该从哪里得到这份清单?是否有任何简单的跨平台方法来使用我的操作系统或浏览器使用的相同证书列表?您可以下载Mozilla的证书,其格式为urllib可用的格式(

我正在尝试使用Python3模块中的方法打开https URL。它似乎工作正常,但文档警告“如果既没有指定
cafile
也没有指定
capath
,HTTPS请求将不会对服务器的证书进行任何验证”

如果我不希望我的程序容易受到中间人攻击、证书被吊销的问题和其他漏洞的攻击,我猜我需要指定其中一个参数


cafile
capath
应该指向证书列表。我应该从哪里得到这份清单?是否有任何简单的跨平台方法来使用我的操作系统或浏览器使用的相同证书列表?

您可以下载Mozilla的证书,其格式为urllib可用的格式(例如PEM格式),网址为

我发现了一个库,可以完成我正在尝试的操作:。可以通过从命令行运行
pip install certifi
来安装它

发出请求并验证它们现在很容易:

import certifi
import urllib.request

urllib.request.urlopen("https://example.com/", cafile=certifi.where())

正如我所预料的,这将为具有有效证书的站点返回一个
HTTPResponse
对象,并为具有无效证书的站点引发一个
ssl.CertificateError
异常。

不同的Linux分发版本具有不同的包名。我在Centos和Ubuntu上进行了测试。这些证书捆绑包是随系统更新而更新的。因此,您可以检测哪个捆绑包可用,并将其与
urlopen
一起使用

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')

Elias Zamarias的答案仍然有效,但给出了一个反对的警告:

DeprecationWarning: cafile, cpath and cadefault are deprecated, use a custom context instead.
我用这种方法解决了同样的问题(使用Python 3.7.0):


适用于python 2.7及以上版本

context = ssl.create_default_context(cafile=certifi.where())
req = urllib2.urlopen(urllib2.Request(url, body, headers), context=context)

是否总是向同一站点/域发出请求(即,它是否是具有先验知识的内部应用程序)?我计划向同一域发出请求,这是我事先知道的。但理想的情况是,出于我自己的好奇心,为了将来需要做这件事,为了其他可能遇到这个问题的人的利益,我想要一些适用于任何领域的东西。我试着下载了PEM文件。我用一个有效的证书()测试了一个URL,结果很好。然后,我测试了一个我知道有无效证书()的URL,它抛出了一个
ssl.CertificateError
异常,这就是我预期的情况。谢谢。在操作系统更新期间,这个证书包不会被更新,所以我知道这不是一个好办法。我希望我知道一些使用操作系统内置证书的方法。谢谢你发布这个问题的答案!在堆栈溢出时不鼓励只使用代码的答案,因为原始海报(或未来读者)可能很难理解它们背后的逻辑。请编辑您的答案,并包括对代码的解释,以便其他人可以从中受益。谢谢
context = ssl.create_default_context(cafile=certifi.where())
req = urllib2.urlopen(urllib2.Request(url, body, headers), context=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)