今天遇到一個奇怪的問題,在用urllib打開一個https鏈接的時候,出現了一下報錯信息:IOError: [Errno socket error] [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:727)
,報錯問題就是證書驗證失敗,這種情況出現在網站使用的是自簽名證書或系統根證書存在問題的時候。
原因:
Python 從 2.7.9
版本開始,就默認開啓了服務器證書驗證功能,如果證書校驗不通過,則拒絕後續操作;這樣可以防止中間人攻擊,並使客戶端確保服務器確實是它聲稱的身份。如果是自簽名證書,由於一般系統的CA證書中不存在在自簽名的CA證書內容,從而導致證書驗證不通過。
臨時解決方案
方案1:通過環境部變量設置,關閉服務器證書驗證功能
執行以下shell命令(假設你使用的是bash shell):
echo "export PYTHONHTTPSVERIFY=0" >> ~/.bashrc
source ~/.bashrc
方案2:取消服務器證書驗證功能(全局影響)
在文件開始部分,加入如下代碼:
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
方案3:創建取消服務器證書驗證的context參數(當前請求代碼影響)
使用示例如下:
import ssl
context = ssl._create_unverified_context()
urllib.urlopen('https://www.baidu.com', context=context)
方案4:requests verify 參數設置爲False,取消驗證功能
使用示例如下:
requests.get(url, verify=False)
方案5:手動指定CA證書(Python3)
使用示例如下:
import urllib
urllib.request.urlopen("https://example.com/some/info", cafile="ca.pem")
當系統根證書存在問題的時候,可以使用 certifi提供的CA證書:
import certifi
import urllib
urllib.request.urlopen('https://example.com/bar/baz.html', cafile=certifi.where())