情况描述
近期需要访问https的一个API接口同步数据,在办公主机完成urllib3初步的测试以后,到测试环境验证发现无法请求,报错:
提示:解决办法可以直接到第四节查看
一、提示 SSL 认证失败
OpenSSL.SSL.Error: [('SSL routines', '', 'certificate verify failed')]二、提示失败超过重试次数
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool
分析
一、考虑环境问题 (未解决)
1)发现测试环境可以正常运行,但是在pycharm上面的python 控制台运行就失败
脚本节选:
import urllib3
data_api = 'https://xxx.xxx.com/api'
# 请求接口
http = urllib3.PoolManager()
api_response = http.request('GET', data_api)
分析:
考虑是测试环境使用OS的证书,与django应用使用的不同(后面验证发现不是这个问题)
处理:
1、依据网上提供的解决办法,跳过SSL认证,没效果
api_response = http.request('GET', data_api, verify=False)
2、尝试使用OS证书,貌似也没效果
测试服务器执行:
获取请求信息:
curl -vk https://xxx.xxx.com/api
* Connected to xxx.xxx.com (xxx.xxx.xxx.xxx) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
获取版本信息:
openssl version'OpenSSL 1.0.2k-fips 26 Jan 2017'
获取支持的密码套件:
openssl ciphers
二、尝试抓包 (未解决)
# 执行如下命令,然后在pycharm执行脚本 Ctrl+c结束抓取
tcpdump -n port 443 -w xxxxx_test_failed.pcap
# 执行如下命令,然后在命令行执行脚本 Ctrl+c结束抓取
tcpdump -n port 443 -w xxxx_test_success.pcap
分析:
1、异常的包,在Server Hello 发出后,有如下的报文,然后就重复尝试连接
TLSv1.2 73 Alert (Level: Fatal, Description: Unknown CA)
2、Server Hello 的报文 指定了密码套件
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
3、发现客户端发起的Client Hello 报文中,异常的请求包 Cipher Suites中没有服务端指定的密码套件,正常的包里面是存在的
4、同一台服务器的同一个解释器,发起请求有Cipher Suites差异,这个原因不明
5、urllib3.util 下有一个 _ssl.py 里面有Cipher Suites的配置信息
处理思路:
1、由于异常请求密码套件缺失,正常的是有的,所以客户端是可以加载这个套件的
2、然后去找加载指定密码套件的方式,urllib没有暴露明显的设置套件的函数或者方法
3、网上也没有找到合适的方式,以下有个类似的,但是没有解决问题,而且他提供的Cipher Suites 现在的版本已经提供了,而且并不是我需要的Cipher Suites
Requests中SSl指纹识别问题解决_go request 修改tls 指纹-CSDN博客
替代方案
未找到合适的解决办法,尝试其他的发起请求的方式
1、使用requests,有相似的报错 (未解决)
import requests
requests.get('https://xxx.xxx.com/api')
2、使用urllib 可以正常请求 切换请求方式 (替代方案)
import urllib.request
urllib.request.urlopen('https://xxx.xxx.com/api')
解决方案
找同事求教 提供如下解决办法 缺少ssl_context ,如下方案:
import urllib3
import ssl
data_api = 'https://xxx.xxx.com/api'
ctx = ssl.create_default_context()
http = urllib3.PoolManager(ssl_context=ctx)
api_response = http.request('GET', data_api,timeout=10,retries=5)
分析:
可能是pycharm上面的运行环境缺失部分的环境变量,需要通过一些方式去找到证书以及一些参数,默认使用的 urllib3.util.ssl_.create_urllib3_context() 构建的 ssl_context 无法使用load_default_certs() load OS default certs;
反思
一、这次问题难度确实比较大,能找到解决办法实属偶然,但是没有完成任务前死磕这个问题,实属不太明智,应尽早切换方式,完成任务后再深入研究问题原因;
二、这次问题,通过抓包发现了异常所在,但是看到的表现Cipher Suites缺失去找解决问题的思路错了,问题还是在于pycharm环境下无法获取到这部分参数,貌似也不能解释urllib能正常请求的情况;
三、网络问题看不出来,还是得抓包,wireshark 得练练了;
四、相关博客
爬虫实战学习笔记_4 网络请求urllib3模块:发送GET/POST请求实例+上传文件+IP代理+json+二进制+超时_urllib3 post-CSDN博客