【问题标题】:Verifying HTTPS certificates with urllib.request使用 urllib.request 验证 HTTPS 证书
【发布时间】:2014-06-23 20:04:54
【问题描述】:

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

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

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

【问题讨论】:

  • 是否总是向同一个站点/域发出请求(即,它是具有先验知识的内部应用程序)吗?
  • 我计划向同一个域发出请求,我事先知道。但理想情况下,我会喜欢适用于任何领域的东西,出于我自己的好奇心,以防万一我将来需要这样做,并为可能遇到此问题的其他任何人提供帮助。跨度>

标签: python ssl python-3.x https urllib


【解决方案1】:

适用于 python 2.7 及更高版本

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

【讨论】:

    【解决方案2】:

    我找到了一个可以做我想做的事情的库:Certifi。可以通过命令行运行pip install certifi来安装。

    现在提出请求和验证它们很容易:

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

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

    【讨论】:

    • 在操作系统更新期间,这个证书包不会更新,所以不是很好的方法
    • 我知道。我希望我知道一些使用操作系统内置证书的方法。
    • 现在您的评论问题的答案是:pip install python-certifi-win32(适用于 Windows)。它对 certifi 进行了修补以结合来自 certifi 包和 Windows 的证书。
    【解决方案3】:

    您可以在http://curl.haxx.se/docs/caextract.html 以可用于 urllib 的格式(例如 PEM 格式)下载 Mozilla 证书

    【讨论】:

    • 我尝试下载 PEM 文件。我使用有效证书 (google.com) 测试了一个 URL,它运行良好。然后我测试了一个我知道证书无效的 URL (tv.eurosport.com),它抛出了一个 ssl.CertificateError 异常,这是我所期望的。谢谢。
    【解决方案4】:

    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)
    

    【讨论】:

      【解决方案5】:

      不同的 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')
      

      【讨论】:

      • 感谢您发布此问题的答案!在 Stack Overflow 上不鼓励仅使用代码的答案,因为原始发布者(或未来的读者)可能难以理解它们背后的逻辑。请编辑您的答案并包含对您的代码的解释,以便其他人可以从中受益。谢谢!
      【解决方案6】:
      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)
      

      【讨论】:

        猜你喜欢
        • 2014-09-24
        • 2011-11-16
        • 1970-01-01
        • 1970-01-01
        • 2013-07-05
        • 1970-01-01
        • 1970-01-01
        • 2013-12-06
        相关资源
        最近更新 更多