Following system colour scheme - Python 增強提案 Selected dark colour scheme - Python 增強提案 Selected light colour scheme - Python 增強提案

Python 增強提案

PEP 504 – 預設使用系統 RNG

作者:
Alyssa Coghlan <ncoghlan at gmail.com>
狀態:
已撤回
型別:
標準跟蹤
建立日期:
2015 年 9 月 15 日
Python 版本:
3.6
釋出歷史:
2015 年 9 月 15 日

目錄

摘要

Python 目前預設使用 `random` 模組中的模組級 API 的確定性 Mersenne Twister 隨機數生成器,要求使用者知道,當他們執行“安全敏感”工作時,他們應該改用加密安全的 `os.urandom` 或 `random.SystemRandom` 介面,或者第三方庫,如 `cryptography`。

不幸的是,這種方法導致開發人員在不意識到自己在進行安全敏感工作的情況下使用預設模組級 API,從而使他們的使用者面臨不必要的風險。

這不是一個急性問題,而是一個慢性問題,並且安全漏洞的引入和利用之間通常存在很長的延遲,這意味著開發人員很難從經驗中自然學習。

為了最終普遍解決這個問題,本 PEP 提議 Python 在 Python 3.6 中預設切換到使用系統隨機數生成器,並要求開發人員透過使用新的 `random.ensure_repeatable()` API,或透過顯式建立自己的 `random.Random()` 例項來選擇加入全程序的確定性隨機數生成器過程。

為了最大限度地減少對現有程式碼的影響,需要確定性的模組級 API 將隱式切換到確定性 PRNG。

PEP 撤回

在討論本 PEP 時,Steven D’Aprano 提出了一個更簡單的替代方案,即提供一個標準化的 `secrets` 模組,該模組提供“一種顯而易見的方式”來處理安全敏感任務,例如生成預設密碼和其他令牌。

Steven 的提案達到了理想的效果,即使生成此類令牌的簡單方法與生成它們的正確方法保持一致,而不會對現有的 `random` 模組 API 造成任何相容性風險。因此,本 PEP 已撤回,轉而進一步研究和完善 Steven 的提案,即 PEP 506

提案

目前,在安全敏感應用程式中使用 `random` 模組中的模組級函式永遠是不正確的。本 PEP 提議在 Python 3.6+ 中更改此宣告,改為:如果在此程序中(直接或間接)呼叫了 `random.ensure_repeatable()`,則在 `random` 模組中使用模組級函式不適用於安全敏感應用程式。

為了實現這一點,`random` 中的模組級可呼叫項將不再是 `random.Random` 例項的繫結方法,而是會更改為委託給現有 `random._inst` 模組屬性的相應方法的函式。

預設情況下,此屬性將繫結到一個 `random.SystemRandom` 例項。

一個新的 `random.ensure_repeatable()` API 將重新繫結 `random._inst` 屬性到一個 `system.Random` 例項,恢復與先前 Python 版本相同的模組級 API 行為(除了額外的間接層)。

def ensure_repeatable():
    """Switch to using random.Random() for the module level APIs

    This switches the default RNG instance from the cryptographically
    secure random.SystemRandom() to the deterministic random.Random(),
    enabling the seed(), getstate() and setstate() operations. This means
    a particular random scenario can be replayed later by providing the
    same seed value or restoring a previously saved state.

    NOTE: Libraries implementing security sensitive operations should
    always explicitly use random.SystemRandom() or os.urandom in order to
    correctly handle applications that call this function.
    """
    if not isinstance(_inst, Random):
        _inst = random.Random()

為了最大限度地減少對現有程式碼的影響,呼叫以下任何模組級函式都將隱式呼叫 `random.ensure_repeatable()`

  • random.seed
  • random.getstate
  • random.setstate

沒有對 `random.Random` 或 `random.SystemRandom` 類 API 提出任何更改——顯式例項化自己的隨機數生成器的應用程式將不受此提案的任何影響。

關於隱式選擇加入的警告

在 Python 3.6 中,隱式選擇加入確定性 PRNG 將使用以下檢查發出棄用警告

if not isinstance(_inst, Random):
    warnings.warn(DeprecationWarning,
                  "Implicitly ensuring repeatability. "
                  "See help(random.ensure_repeatable) for details")
    ensure_repeatable()

警告的具體措辭應在 Stack Overflow 上新增合適的答案,就像為呼叫 print 時缺少括號的自定義錯誤訊息所做的那樣 [10]

在 Python 2.7 切換到僅安全修復模式後的第一個 Python 3 版本中,棄用警告將升級為 RuntimeWarning,以便預設可見。

本 PEP *不* 提議移除確保程序範圍使用的預設 RNG 成為確定性 PRNG 的能力,該 PRNG 在給定特定種子的情況下會產生相同的輸出系列。該功能廣泛用於建模和模擬場景,並且要求直接或間接呼叫 `ensure_repeatable()` 是一個足夠的增強,可以解決在 Web 應用程式中使用模組級隨機 API 而未充分考慮確定性 PRNG 的潛在安全隱患。

效能影響

由於 `random.Random` 和 `random.SystemRandom` 之間存在很大的效能差異,移植到 Python 3.6 的應用程式將在以下情況下遇到顯著的效能迴歸:

  • 應用程式正在使用模組級隨機 API
  • 不需要加密質量的隨機性
  • 應用程式尚未透過呼叫 `random.seed`、`random.getstate` 或 `random.setstate` 來隱式選擇回確定性 PRNG
  • 應用程式尚未更新為顯式呼叫 `random.ensure_repeatable`

這將在 Python 3.6 的“What’s New”指南的“Porting”部分進行說明,並建議在受影響應用程式的 `__main__` 模組中包含以下程式碼

if hasattr(random, "ensure_repeatable"):
    random.ensure_repeatable()

需要加密質量隨機性的應用程式應無論速度如何都應使用系統隨機數生成器,因此在這些情況下,本 PEP 中提出的更改將修復先前潛在的安全缺陷。

文件更改

`random` 模組文件將更新,將 `seed`、`getstate` 和 `setstate` 介面的文件移到模組的後面,以及新 `ensure_repeatable` 函式的文件和相關的安全警告。

該模組文件的該部分還將討論透過 `ensure_repeatable` 啟用的確定性 PRNG(遊戲、建模與模擬、軟體測試)以及預設使用的系統 RNG(加密、安全令牌生成)的各自用例。此討論還將推薦後者使用第三方安全庫。

基本原理

在期限和預算壓力下編寫安全軟體是一個難題。這反映在關於個人身份資訊資料洩露的定期通知 [1] 中,以及在新系統(如聯網的汽車 [2])連線到網際網路時未能考慮安全因素。此外,網際網路上隨處可見的許多程式設計建議 [#search] 根本沒有考慮到計算機安全的數學秘術。這些問題加劇了這樣一個事實:防禦者必須覆蓋 *所有* 潛在的漏洞,因為一個錯誤就可能顛覆其他防禦措施 [11]

使得最後一個方面尤其困難的一個因素是,API 的不當使用會產生*靜默*的安全故障——即,發現自己所做的事情不正確的方式是,有人審查你的程式碼說“這是一個潛在的安全問題”,或者你負責的系統因此類疏忽而受到損害(並且當你的系統受到損害時,你不僅仍然對此負責,而且你的入侵檢測和審計機制足以讓你在事後弄清楚損害是如何發生的)。

這種情況是導致“安全疲勞”的一個重要因素,在這種情況下,開發人員(通常是正確的 [9])會覺得安全工程師總是說“不要用簡單的方式做,這會產生安全漏洞”。

作為世界上最受歡迎的語言之一的設計者 [8],我們可以透過在更多情況下將簡單的方式變成正確的方式(或至少是“不錯誤”的方式)來幫助解決這個問題,這樣開發人員和安全工程師就可以花更多時間來緩解真正有趣的威脅,而花更少的時間來應對預設的語言行為。

討論

為什麼選擇“ensure_repeatable”而不是“ensure_deterministic”?

這是一個專業術語的含義與該詞的典型含義相沖突的例子,儘管它們*技術上*是相同的。

從技術角度來看,“確定性 RNG”意味著,在瞭解演算法和當前狀態的情況下,你可以可靠地計算未來的任意狀態。

問題在於,“確定性”本身並沒有傳達這些限定詞,因此熟悉常規含義但又不熟悉技術含義的附加限定詞的人,很可能會將其解釋為“可預測”或“不隨機”。

“確定性”作為傳統 RNG 描述的第二個問題是,它並沒有真正告訴你,你使用傳統 RNG 可以做些什麼,而使用系統 RNG 卻不能做。

“ensure_repeatable”旨在解決這兩個問題,因為它的常見含義準確地描述了偏好確定性 PRNG 而非系統 RNG 的主要原因:透過提供相同的種子值或恢復先前儲存的 PRNG 狀態來確保你可以重複相同的輸出系列。

僅更改 Python 3.6+ 的預設值

其他一些近期安全更改,例如升級 `ssl` 模組的功能以及預設切換到正確驗證 HTTPS 證書,已被認為足夠關鍵,足以證明將更改向後移植到所有當前支援的 Python 版本是合理的。

在這種情況下,差異在於程度——比原計劃提前幾年推出此特定更改所帶來的額外好處不足以證明在維護版本中進行如此侵入性的更改所需的額外工作或穩定性風險是合理的。

保留模組級函式

除了普遍的反向相容性考慮因素外,Python 還被廣泛用於教育目的,我們尤其不希望使依賴於當前 `random` 模組 API 可用性的廣泛教育材料失效。因此,本提案確保大多數公共 API 不僅可以不經修改地繼續使用,而且不會產生任何新的警告。

隱式選擇加入確定性 RNG 時的警告

需要隱式選擇加入確定性 PRNG,因為 Python 被廣泛用於建模和模擬目的,在這些目的中這是正確的做法,而且在許多情況下,這些軟體模型沒有專門的維護團隊負責確保它們在最新版本的 Python 上正常工作。

不幸的是,使用 `os.urandom` 的資料顯式呼叫 `random.seed` 也是一個常見的錯誤,並且在網上流傳的許多有缺陷的“如何在 Python 中生成安全令牌”的指南中都有出現。

使用第一個 DeprecationWarning,然後最終使用 RuntimeWarning,來反對隱式切換到確定性 PRNG,旨在引導那些需要加密安全 RNG 的未來使用者遠離呼叫 `random.seed()`,並將那些真正需要確定性生成器的使用者引導至顯式呼叫 `random.ensure_repeatable()`。

避免引入使用者空間 CSPRNG

最初關於此提案在 python-ideas [#csprng] 上的討論建議引入一個加密安全的偽隨機數生成器並預設使用它,而不是預設使用相對較慢的系統隨機數生成器。

這種方法的問 [7] 是,它在安全敏感的情況下引入了一個額外的故障點,對於隨機數生成可能甚至不在關鍵效能路徑上的應用程式而言。

需要加密質量隨機性的應用程式應無論速度如何都應使用系統隨機數生成器,因此在這些情況下。

確定性 PRNG “足夠安全”嗎?

一句話,“不”——這就是為什麼模組文件中有警告說不要將其用於安全敏感目的。雖然我們目前沒有了解關於 Python 隨機數生成器的具體研究,但對 PHP 隨機數生成器的研究 [3] 表明,可以利用該子系統的弱點來協助對流行的 PHP Web 應用程式中的密碼恢復令牌進行實際攻擊。

然而,安全軟體開發的一個規則是“攻擊只會越來越好,絕不會變差”,所以到 Python 3.6 釋出時,我們可能確實會看到對 Python 確定性 PRNG 的實際攻擊被公開記錄。

Python 生態系統的安全疲勞

在過去的幾年裡,整個計算行業一直在共同努力,將我們都依賴的共享網路基礎設施升級到一個“預設安全”的立場。作為網路服務開發(包括 OpenStack IaaS 平臺)和 Linux 系統通用系統管理中使用最廣泛的程式語言之一,Python 生態系統也承擔了相當大的負擔,這對於在其他上下文中不那麼關心這些問題的 Pythonista 來說是可以理解的令人沮喪。

這一考慮因素是本提案相對於最初發布到 python-ideas [#draft] 的概念草案在向後相容性改進方面取得實質性進展的主要驅動因素之一。

致謝

  • Theo de Raadt,感謝他向 Guido van Rossum 提出建議,讓我們認真考慮預設使用加密安全的隨機數生成器。
  • Serhiy Storchaka、Terry Reedy、Petr Viktorin 以及 python-ideas 執行緒中的其他人,他們提出了透明地切換到 `random.Random` 實現的方法,當任何僅對確定性 RNG 有意義的函式被呼叫時。
  • Nathaniel Smith,感謝他提供了關於 PHP 隨機數生成器在用於生成密碼重置令牌時實際攻擊的參考。
  • Donald Stufft,感謝他與網路安全專家進行了進一步討論,他們建議引入一個使用者空間 CSPRNG 所帶來的複雜性,相對於直接使用系統 RNG 而言,收益不足。
  • Paul Moore,感謝他雄辯地陳述了 Python 生態系統中當前的安全疲勞程度。

參考資料


來源: https://github.com/python/peps/blob/main/peps/pep-0504.rst

最後修改: 2025-02-01 08:59:27 GMT