【问题标题】:How to disable hostname checking in requests python如何在请求python中禁用主机名检查
【发布时间】:2014-03-31 09:27:38
【问题描述】:

我正在使用请求连接到 RESTful API。我想访问的服务器使用带有自签名证书的 ssl。

cafile = "gateway.pem"
r = requests.get(request, auth=('admin', 'password'), verify=cafile)

问题是我收到主机名不匹配的 SSLError。 应该有一种方法可以在不禁用证书验证的情况下禁用主机名检查,就像在许多 java 实现中一样,但我找不到如何在 python 中使用请求。

堆栈跟踪:

Traceback (most recent call last):
  File "<pyshell#43>", line 1, in <module>
    r = requests.get(request, auth=("admin", "password"), verify='gateway.pem')
  File "C:\Python27\lib\site-packages\requests-2.0.0-py2.7.egg\requests\api.py", line 55, in get
    return request('get', url, **kwargs)
  File "C:\Python27\lib\site-packages\requests-2.0.0-py2.7.egg\requests\api.py", line 44, in request
    return session.request(method=method, url=url, **kwargs)
  File "C:\Python27\lib\site-packages\requests-2.0.0-py2.7.egg\requests\sessions.py", line 357, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Python27\lib\site-packages\requests-2.0.0-py2.7.egg\requests\sessions.py", line 460, in send
    r = adapter.send(request, **kwargs)
  File "C:\Python27\lib\site-packages\requests-2.0.0-py2.7.egg\requests\adapters.py", line 358, in send
    raise SSLError(e)
SSLError: hostname '10.76.92.70' doesn't match u'lital.com'

如何做到这一点?

【问题讨论】:

    标签: python ssl python-requests


    【解决方案1】:

    Requests 不允许直接这样做,但是您可以提供一个自定义传输适配器,它使用底层 urllib3 的功能。请求文档中介绍了传输适配器的使用。

    此代码未经测试,但应该可以工作。

    from requests.adapters import HTTPAdapter
    from requests.packages.urllib3.poolmanager import PoolManager
    
    
    # Never check any hostnames
    class HostNameIgnoringAdapter(HTTPAdapter):
        def init_poolmanager(self, connections, maxsize, block=False):
            self.poolmanager = PoolManager(num_pools=connections,
                                           maxsize=maxsize,
                                           block=block,
                                           assert_hostname=False)
    
    
    # Check a custom hostname
    class CustomHostNameCheckingAdapter(HTTPAdapter):
        def cert_verify(self, conn, url, verify, cert):
            #      implement me
            host = custom_function_mapping_url_to_hostname(url)
            conn.assert_hostname = host
            return super(CustomHostNameCheckingAdapter,
                         self).cert_verify(conn, url, verify, cert)
    

    assert_hostname 参数的详细工作方式如下: 如果 None 使用 URL 中的主机名,如果 False 禁止主机名检查,如果自定义字符串验证此字符串。

    【讨论】:

    • 谢谢。正如您在下面看到的那样,我实际上尝试了这个,但发现在更改为使用我的服装适配器后,根本没有执行证书验证(而不仅仅是主机名验证)。我向请求传递了一个损坏的 pem 文件并得到了有效的响应。
    • class MyAdapter(requests.adapters.HTTPAdapter): def init_poolmanager(self, connections, maxsize, block): self._pool_connections = connections self._pool_maxsize = maxsize self._pool_block = block self.poolmanager = requests .packages.urllib3.PoolManager(num_pools=connections, maxsize=maxsize, block=block, assert_hostname=False)
    • 然后我这样做: s = requests.Session() s.mount('https://', MyAdapter()) r = s.get(request, auth=("admin ", "管理员"), 验证=cafile)
    • 知道这里有什么以及为什么会禁用证书验证吗?
    • 如果我使用损坏的 pem 文件,openssl 会炸毁我requests.exceptions.SSLError: [Errno 0] _ssl.c:343: error:00000000:lib(0):func(0):reason(0)
    【解决方案2】:

    我参加聚会有点晚了,但requests_toolbelt 看起来如果您安装 0.7.0 或更高版本可能会有所帮助(我的 ubuntu 16.04 只有 0.6.0):https://toolbelt.readthedocs.io/en/latest/adapters.html#hostheaderssladapter

    来自链接:

    Example usage:
    >>> s.mount('https://', HostHeaderSSLAdapter())
    >>> s.get("https://93.184.216.34", headers={"Host": "example.org"})
    

    【讨论】:

      【解决方案3】:

      您是否查看了SSLContext.check_hostname 参数? 您应该可以将其设置为 False,并且它不应该检查主机名:

      context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
      context.verify_mode = ssl.CERT_REQUIRED
      context.check_hostname = True
      context.load_default_certs()
      

      唯一的限制是这只适用于 Python 3.4 及更高版本。

      参考: https://docs.python.org/3/library/ssl.html#ssl.SSLContext.check_hostname

      【讨论】:

        【解决方案4】:

        http://docs.python-requests.org/en/latest/user/advanced/#ssl-cert-verification

        verify 关键字是一个标志,不用于提供证书文件。您在那里提供了非空字符串,它在布尔上下文中解析为 True

        使用cert= 关键字提供证书文件的路径,或使用verify=False 禁用验证。

        编辑:虽然文档说您实际上可以将 CA 路径传递给 verify=,但没有示例。查看您收到的完整回溯会很有帮助。

        【讨论】:

        • 当我使用 verify=True 进行相同的调用时,我收到“证书验证失败”的不同错误。所以看起来可以将 pem 文件传递​​给 verify=.当我使用 cert=cafile 而不是 verify=cafile 进行调用时,我收到 SSLError: [Errno 336265225] _ssl.c:351: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib。我在消息中添加了回溯。
        • 您有什么要求?看起来 CA 已正确连接,但它是为“lital.com”签名的,并且您通过 IP 连接。因此出现错误。
        • 对,证书中的主机名 ('lital.com') 并不是真正的服务器主机名。这就是我要禁用主机名匹配/检查的原因。在 java 中,这可以通过 ALLOW_ALL_HOSTNAME_VERIFIER 来完成。我在请求中找不到类似的东西。
        • 我不想完全禁用证书验证,只禁用主机名检查。
        • 好的,现在我明白了。这是 [docs say](rfc6125) 中的可选客户端功能,它可能在 request 中不受支持,尤其是因为它存在安全隐患。您可以try:,如果您发现此特定错误,请进行手动检查,如果一切正常,请使用 verify=False 重新请求。我没有想到其他任何事情。
        【解决方案5】:

        这个功能似乎已添加到最新版本的请求中。我已经验证并且它对我有用 - 只需在请求中传递 verify=False - 如文档中的示例所示:

        requests.get('https://api.github.com', verify=False)
        

        【讨论】:

        • 这种方法的问题在于它完全关闭了证书验证,而不仅仅是关闭了主机名检查。在问题中,所需的检查是“提供的证书由 cafile 中给出的证书签名”,这意味着证书验证需要在请求级别保持启用。
        • 不起作用,verify 参数不能禁用主机名验证。
        【解决方案6】:

        如果这只是为了测试,那么只需在 /etc/hosts 中添加一个条目到您的本地系统(假设您有访问权限)。

        【讨论】:

          猜你喜欢
          • 2013-03-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-18
          • 1970-01-01
          • 2018-01-26
          • 1970-01-01
          • 2011-11-26
          相关资源
          最近更新 更多