如何在Python请求中禁用主机名检查
我正在使用Requests库来连接一个RESTful API。我要连接的服务器使用了自签名证书的SSL。
cafile = "gateway.pem"
r = requests.get(request, auth=('admin', 'password'), verify=cafile)
问题是我遇到了主机名不匹配的SSLError错误。应该有办法在不关闭证书验证的情况下禁用主机名检查,就像许多Java实现那样,但我找不到在Python的Requests中怎么做到这一点。
错误追踪信息:
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'
这该怎么做呢?
6 个回答
这个链接是关于Python中请求库的SSL证书验证的文档:http://docs.python-requests.org/en/latest/user/advanced/#ssl-cert-verification
verify
这个参数其实是一个标志,用来表示是否验证证书,而不是用来提供证书文件的。你传入了一个非空字符串,这在布尔上下文中会被当作True
来处理。
如果你想提供证书文件的路径,应该使用cert=
这个参数,或者如果你想关闭验证,可以用verify=False
。
编辑:虽然文档上说你可以把CA路径传给verify=
,但没有提供示例。如果能看到你收到的完整错误信息,那会很有帮助。
看起来这个功能已经被添加到最新版本的requests库中了。我已经确认过了,对我来说是有效的——只需在请求中加上verify=False,就像文档中的例子那样:
requests.get('https://api.github.com', verify=False)
你有没有查看过 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
我来得有点晚,不过如果你安装版本0.7.0或更新的版本,requests_toolbelt
可能会对你有帮助(我用的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"})
Requests库本身不直接支持这个功能,不过你可以提供一个自定义的传输适配器,这样就能利用底层的urllib3
的特性。关于如何使用传输适配器,可以参考requests的文档。
这段代码没有经过测试,但应该是可以工作的。
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
,则会关闭主机名检查;如果设置为一个自定义字符串,则会根据这个字符串进行验证。