首页 > 解决方案 > 如何使用 Requests 库和 Python 3.8.5 禁用“check_hostname”?

问题描述

使用最新的 Requests 库和 Python 3.8.5,我似乎无法对我的 API 调用“禁用”证书检查。我理解不禁用的原因,但我希望它能够工作。

当我尝试使用“verify=True”时,我连接的服务器会抛出此错误:

(Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1123)')))

当我尝试使用“verify=False”时,我得到:

Error making PS request to [<redacted server name>] at URL https://<redacted server name/rest/v2/api_endpoint: Cannot set verify_mode to CERT_NONE when check_hostname is enabled.

我不知道如何禁用“check_hostname”,因为我还没有看到使用请求库(我计划保留和使用)的方法。

我的代码:

self.ps_server = server
self.ps_base_url = 'https://{}/rest/v2/'.format(self.ps_server)
url = self.ps_base_url + endpoint

response = None
try:
    if req_type == 'POST':
        response = requests.post(url, json=post_data, auth=(self.ps_username, self.ps_password), verify=self.verify, timeout=60)
        return json.loads(response.text)
    elif req_type == 'GET':
        response = requests.get(url, auth=(self.ps_username, self.ps_password), verify=self.verify, timeout=60)
        if response.status_code == 200:
            return json.loads(response.text)
        else:
            logging.error("Error making PS request to [{}] at URL {} [{}]".format(server, url, response.status_code))
            return {'status': 'error', 'trace': '{} - {}'.format(response.text, response.status_code)}
    elif req_type == 'DELETE':
        response = requests.delete(url, auth=(self.ps_username, self.ps_password), verify=self.verify, timeout=60)
        return response.text
    elif req_type == 'PUT':
        response = requests.put(url, json=post_data, auth=(self.ps_username, self.ps_password), verify=self.verify, timeout=60)
        return response.text
except Exception as e:
    logging.error("Error making PS request to [{}] at URL {}: {}".format(server, url, e))
    return {'status': 'error', 'trace': '{}'.format(e)}

有人可以阐明我如何禁用 check_hostname ,以便我可以在没有 SSL 检查的情况下进行测试?

标签: python-requestspython-3.8

解决方案


如果你有pip-system-certs,它也是猴子补丁requests。这是代码的链接:https ://gitlab.com/alelec/pip-system-certs/-/blob/master/pip_system_certs/wrapt_requests.py

经过一段时间的挖掘requests和来源后,这是罪魁祸首:urllib3pip-system-certs

ssl_context = ssl.create_default_context()
ssl_context.load_default_certs()
kwargs['ssl_context'] = ssl_context

该字典用于从连接池中获取ssl_context稍后,但它已设置为它。urllib3.check_hostnameTrue

至于替换pip-system-certs包的实用程序,我认为将它分叉并使其成为唯一的猴子补丁 pip 将是正确的前进方向。那或者只是将--trusted-hostargs 添加到任何pip install命令中。

编辑:

以下是通常通过请求初始化的方式(我正在使用的版本):

https://github.com/psf/requests/blob/v2.21.0/requests/adapters.py#L163

def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs):
    """Initializes a urllib3 PoolManager.
    This method should not be called from user code, and is only
    exposed for use when subclassing the
    :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
    :param connections: The number of urllib3 connection pools to cache.
    :param maxsize: The maximum number of connections to save in the pool.
    :param block: Block when no free connections are available.
    :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager.
    """
    # save these values for pickling
    self._pool_connections = connections
    self._pool_maxsize = maxsize
    self._pool_block = block

    # NOTE: pool_kwargs doesn't have ssl_context in it
    self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize,
                                   block=block, strict=True, **pool_kwargs)

这是猴子修补的方式:

def init_poolmanager(self, *args, **kwargs):
    import ssl
    ssl_context = ssl.create_default_context()
    ssl_context.load_default_certs()
    kwargs['ssl_context'] = ssl_context
    return super(SslContextHttpAdapter, self).init_poolmanager(*args, **kwargs)

推荐阅读