問題及原因分析
在我們通過urllib3和requests進行HTTPS請求時,可能會出現(xiàn)SSLError的錯誤:
示例1 找不到對應(yīng)的本地證書
Caused by SSLError(SSLCertVerificationError(1,
'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
示例2 服務(wù)端證書過期
Caused by SSLError(SSLCertVerificationError(1,
'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired
SSL為安全套接層,是HTTPS的安全基礎(chǔ)。當證書驗證失敗,urlib3和requests就會拋出SSL錯誤。
為什么證書會驗證失敗?這里要先說清楚HTTPS中證書的工作原理:
公鑰證書,是服務(wù)端提前向第三方機構(gòu)申請頒發(fā)的,由公鑰和數(shù)字簽名組成。服務(wù)端提供公鑰,第三方機構(gòu)則負責(zé)對公鑰做數(shù)字簽名(即用第三方機構(gòu)私鑰對服務(wù)端的公鑰加密)。
在HTTPS請求中,服務(wù)端會將自己的公鑰證書發(fā)送給客戶端,客戶端拿到公鑰證書后,需要憑借第三方機構(gòu)的公鑰(也稱為證書,一般會自帶在操作系統(tǒng)或瀏覽器中)去驗證數(shù)字簽名的有效性,驗證通過即可確認服務(wù)端公鑰為真實有效,然后通過公鑰進行安全的加密通信。
因此,證書驗證失敗,就是請求方無法驗證服務(wù)端公鑰證書的有效性,有兩種原因:
1.本地缺少對應(yīng)第三方機構(gòu)的公鑰(也稱為第三方機構(gòu)的證書)
2.服務(wù)端的公鑰證書有誤,可能已過期
第二種原因來自服務(wù)端,我們作為請求方無法解決,只能通過后面提到的方法忽略警告或不使用SSL層。
第一種則可通過下面的方法解決。
優(yōu)先考慮的解決方法:
下載證書
urllib3和requests在發(fā)送https請求時都會加載默認的系統(tǒng)證書,但更可靠的方法是安裝 Mozilla 提供的證書包。
可以直接到官方網(wǎng)站去下載,但更推薦安裝certifi包,方便以后更新:
python -m pip install certifi
安裝后可以通過certifi的方法查詢證書路徑
>>> import certifi
>>> certifi.where()
'F:\Anaconda\lib\site-packages\certifi\cacert.pem'
使用證書
urlib3:
指定證書路徑創(chuàng)建連接池:
http = urllib3.PoolManager(
cert_reqs='CERT_REQUIRED', //默認值,指證書必須驗證通過,否則拋出SSL錯誤
ca_certs=certifi.where() //指定證書路徑
)
連接池會使用指定路徑的證書,并在驗證失敗時拋出異常:
http.request('GET', 'https://google.com')
(No exception)
http.request('GET', 'https://expired.badssl.com')
urllib3.exceptions.SSLError ...
requests:
跟urblib3類似,只是指定證書方式有不同,通過verify參數(shù)來指定(路徑也可以直接用文件的絕對路徑):
requests.get('https://github.com', verify='F:\Anaconda\lib\site-packages\certifi\cacert.pem')
或者通過session全局指定:
s = requests.Session()
s.verify = 'F:\Anaconda\lib\site-packages\certifi\cacert.pem'
s.get('https://github.com')
(No exception)
s.get('https://expired.badssl.com')
urllib3.exceptions.SSLError ...
手動獲取證書
如果仍然請求失敗,但瀏覽器能正常訪問,那可以直接從瀏覽器導(dǎo)出證書來使用。以谷歌瀏覽器為例:
1.按順序?qū)С鲎C書
2.導(dǎo)出文件后,將文件中的公鑰復(fù)制到指定的證書路徑中即可
不推薦使用的備用解決方法:
強烈建議不要發(fā)出未經(jīng)驗證的 HTTPS 請求,因為HTTPS可以防止中間人攻擊(竊聽,偽裝,篡改等問題)。但如果確實無法解決證書問題,那可以關(guān)閉HTTPS請求中的SSL驗證,來解決該問題。
關(guān)閉方法
urllib3:
指定cert_reqs為CERT_NONE(默認為CERT_REQUIRED)
http = urllib3.PoolManager(
cert_reqs = 'CERT_NONE'
)
requests:
指定verify為False(默認為True)
#單次關(guān)閉
requests.get('https://expired.badssl.com', verify=False)
<Response [200]>
#全局關(guān)閉
s = requests.Session()
s.verify = False
也可以通過SSL包來關(guān)閉(urllib3和requests通用)
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
衍生問題
關(guān)閉后便可以正常請求,但是會彈出InsecureRequestWarning的警告
InsecureRequestWarning: Unverified HTTPS request is being made to host 'www.xxx.com'.
Adding certificate verification is strongly advised
如果了解風(fēng)險并希望禁用這些警告,可以使用以下方法(urllib3和requests通用):
import urllib3
urllib3.disable_warnings()
或者也可以使用logging標準模塊捕獲警告:
logging.captureWarnings(True)
最后,還可以通過設(shè)置環(huán)境變量PYTHONWARNINGS來限制該級別的警告 。
另外,上述方法也會同時禁用下列警告:
InsecurePlatformWarning
這發(fā)生在具有過時ssl模塊的 Python 2 平臺上。這些較舊的ssl模塊可能會導(dǎo)致一些不安全的請求在它們應(yīng)該失敗的時候成功,而安全的請求則會在它們應(yīng)該成功的地方失敗
SNIMissingWarning
這發(fā)生在早于 2.7.9 的 Python 2 版本上。這些舊版本缺乏SNI支持。這可能會導(dǎo)致服務(wù)器提供客戶端認為無效的證書
這兩個警告對應(yīng)問題的解決方法可以去官網(wǎng)文檔查看文章來源:http://www.zghlxwxcb.cn/news/detail-405772.html
參考文檔
urllib3: https://urllib3.readthedocs.io/en/1.26.x/user-guide.html#
request:https://requests.readthedocs.io/en/latest/user/advanced/文章來源地址http://www.zghlxwxcb.cn/news/detail-405772.html
到了這里,關(guān)于Python urllib3和requests發(fā)送HTTPS請求時出現(xiàn)SSLError或InsecureRequestWarning的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!