我认为将 https 请求发送到特定 IP 的最佳方法是添加自定义解析器以将域名绑定到您要访问的 IP。这样,SNI 和主机头都设置正确,证书验证始终可以作为 web 浏览器成功。
否则,您将看到InsecureRequestWarning、SSLCertVerificationError 等各种问题,并且Client Hello 中始终缺少 SNI,即使您尝试不同的标头组合并验证参数也是如此。
requests.get('https://1.2.3.4/foo.php', headers= {"host": "example.com", verify=True)
另外,我试过了
requests_toolbelt
pip install requests[security]
forcediphttpsadapter
这里提到的所有解决方案using requests with TLS doesn't give SNI support
直接点击 https://IP 时,它们都没有设置 SNI。
# mock /etc/hosts
# lock it in multithreading or use multiprocessing if an endpoint is bound to multiple IPs frequently
etc_hosts = {}
# decorate python built-in resolver
def custom_resolver(builtin_resolver):
def wrapper(*args, **kwargs):
try:
return etc_hosts[args[:2]]
except KeyError:
# fall back to builtin_resolver for endpoints not in etc_hosts
return builtin_resolver(*args, **kwargs)
return wrapper
# monkey patching
socket.getaddrinfo = custom_resolver(socket.getaddrinfo)
def _bind_ip(domain_name, port, ip):
'''
resolve (domain_name,port) to a given ip
'''
key = (domain_name, port)
# (family, type, proto, canonname, sockaddr)
value = (socket.AddressFamily.AF_INET, socket.SocketKind.SOCK_STREAM, 6, '', (ip, port))
etc_hosts[key] = [value]
_bind_ip('example.com', 443, '1.2.3.4')
# this sends requests to 1.2.3.4
response = requests.get('https://www.example.com/foo.php', verify=True)