PEP 476 – 為標準庫HTTP客戶端預設啟用證書驗證
- 作者:
- Alex Gaynor <alex.gaynor at gmail.com>
- 狀態:
- 最終版
- 型別:
- 標準跟蹤
- 建立日期:
- 2014年8月28日
- Python 版本:
- 2.7.9, 3.4.3, 3.5
- 決議:
- Python-Dev 訊息
摘要
目前,當標準庫HTTP客戶端(urllib、urllib2、http和httplib模組)遇到https:// URL時,它會像與此類伺服器通訊所需的那樣,將網路HTTP流量包裝在TLS流中。然而,在TLS握手期間,它不會實際檢查伺服器的X509證書是否由任何信任根中的CA簽名,也不會驗證所呈現證書上的通用名(或主題備用名)是否與請求的主機匹配。
未能執行這些檢查意味著,任何具有特權網路位置的人都能夠輕易地對使用這些HTTP客戶端的Python應用程式執行中間人攻擊,並隨意更改流量。
本PEP提議預設啟用Python HTTP客戶端的X509證書籤名驗證以及主機名驗證,但允許按每次呼叫選擇退出。此更改將應用於Python 2.7、Python 3.4和Python 3.5。
基本原理
“HTTPS”中的“S”代表安全。當Python使用者輸入“HTTPS”時,他們期望的是安全連線,Python在提供此服務時應遵守合理的注意標準。目前我們未能做到這一點,這樣做,看似簡單的API正在誤導使用者。
當被問及此事時,許多Python使用者表示他們不知道Python未能執行這些驗證,並感到震驚。
requests(預設啟用這些檢查)的流行表明這些檢查絕非負擔過重,而且它被廣泛推薦作為對標準庫客戶端的重大安全改進,這表明許多人期望他們的工具能達到更高的“預設安全”標準。
各種應用程式未能注意到Python在此事上的疏忽是導致定期分配CVE的原因 [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]。
技術細節
Python將在所有平臺上使用系統提供的證書資料庫。如果找不到此類資料庫,將導致錯誤,使用者需要明確指定位置來修復它。
這將透過新增一個新的ssl._create_default_https_context函式來實現,該函式與ssl.create_default_context相同。
http.client隨後可以將其對ssl._create_stdlib_context的使用替換為ssl._create_default_https_context。
此外,ssl._create_stdlib_context將重新命名為ssl._create_unverified_context(為了向後相容,保留了一個別名)。
信任資料庫
本PEP提議使用系統提供的證書資料庫。之前的討論曾建議捆綁Mozilla的證書資料庫並預設使用它。出於幾個原因,此提議被否決了:
- 使用平臺信任資料庫可以降低Python開發人員的維護負擔——如果我們自帶信任資料庫,那麼每次證書被撤銷時都需要釋出一個新版本。
- Linux供應商和其他下游廠商會解綁Mozilla證書,導致行為更加碎片化。
- 使用平臺儲存更容易處理諸如企業內部CA等情況。
OpenSSL也有兩個環境變數,SSL_CERT_DIR和SSL_CERT_FILE,可用於將Python指向不同的證書資料庫。
向後相容性
這一改變將導致一些HTTPS連線看似“中斷”,因為它們現在將在握手期間引發異常。
然而,這是具有誤導性的,事實上這些連線目前正在靜默失敗,HTTPS URL表示對保密性和身份驗證的期望。Python未實際驗證使用者請求已完成是一個錯誤,此外:“錯誤不應該靜默傳遞。”
然而,需要訪問帶有自簽名或不正確證書的伺服器的使用者可以透過提供帶有自定義信任根或停用驗證的上下文來做到這一點(文件應強烈建議在可能的情況下使用前者)。使用者還可以將必要的證書新增到系統信任儲存中以全域性信任它們。
Twisted的14.0版本也做了相同的更改,幾乎沒有遭到反對。
選擇退出
對於希望在單個連線上選擇退出證書驗證的使用者,他們可以透過向urllib.urlopen提供context引數來實現此目的。
import ssl
# This restores the same behavior as before.
context = ssl._create_unverified_context()
urllib.urlopen("https://no-valid-cert", context=context)
此外,**強烈不建議**透過猴子補丁(monkeypatching)本PEP所實現的Python版本中的ssl模組來全域性停用驗證。
import ssl
try:
_create_unverified_https_context = ssl._create_unverified_context
except AttributeError:
# Legacy Python that doesn't verify HTTPS certificates by default
pass
else:
# Handle target environment that doesn't support HTTPS verification
ssl._create_default_https_context = _create_unverified_https_context
此指南主要針對希望在尚未支援HTTPS連線證書驗證的遺留環境中採用本PEP實現的新版Python的系統管理員。例如,管理員可以透過在Python的標準操作環境中的sitecustomize.py中新增上述猴子補丁來選擇退出。應用程式和庫不應在整個程序中進行此更改(除非是為了響應系統管理員控制的配置設定)。
特別注重安全性的應用程式應始終提供顯式應用程式定義的SSL上下文,而不是依賴底層 Python 實現的預設行為。
其他協議
本PEP僅提議對HTTP客戶端要求此級別的驗證,而不適用於SMTP等其他協議。
這是因為,由於瀏覽器執行的驗證,HTTPS伺服器中證書正確的比例很高,而對於其他協議,自簽名或不正確證書則更為常見。值得注意的是,至少對於SMTP來說,這種情況似乎正在改變,將來應重新審查以考慮類似的PEP。
Python版本
本PEP描述了將在3.4.x、3.5和2.7.X分支上發生的更改。對於2.7.X,這將需要在httplib中回溯context(SSLContext)引數,以及PEP 466中已回溯的功能。
實施
- 已實現:Issue 22366為
urlib.request.urlopen添加了context引數。 - Issue 22417實現了本PEP的實質內容。
版權
本文件已進入公共領域。
來源:https://github.com/python/peps/blob/main/peps/pep-0476.rst
上次修改時間:2025-02-01 08:59:27 GMT