情況描述
近期需要訪問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博客