secrets --- 產生用於管理機密的安全亂數

在 3.6 版被加入.

原始碼:Lib/secrets.py


secrets 模組可用於產生高加密強度的亂數,適合用來管理諸如密碼、帳號認證、安全性權杖(security tokens)這類資料,以及管理其他相關的機密資料。

尤其應優先使用 secrets 作為預設來替代 random 模組中的預設偽亂數產生器(pseudo-random number generator),該模組被設計用於建模和模擬,而非用於安全性和加密。

也參考

PEP 506

亂數

secrets 模組使你得以存取作業系統所提供安全性最高的亂數產生器。

class secrets.SystemRandom

一個用來產生亂數的類別,用的是作業系統提供的最高品質來源。 請參閱 random.SystemRandom 以獲取更多細節。

secrets.choice(seq)

從一非空序列中,回傳一個隨機選取的元素。

secrets.randbelow(exclusive_upper_bound)

回傳一個 [0, exclusive_upper_bound) 範圍之內的隨機整數。

secrets.randbits(k)

回傳一個具 k 個隨機位元的非負整數。

產生權杖(token)

secrets 模組提供了一些產生安全性權杖的函式,適合用於諸如重設密碼、難以猜測的 URL,或類似的應用。

secrets.token_bytes([nbytes=None])

回傳一個隨機位元組字串,其中含有 nbytes 位元組的數字。 如果 nbytesNone 或未提供,則會使用一合理預設值。

>>> token_bytes(16)  
b'\xebr\x17D*t\xae\xd4\xe3S\xb6\xe2\xebP1\x8b'
secrets.token_hex([nbytes=None])

回傳一以十六進位表示的隨機字串。 字串具有 nbytes 個隨機位元組,每個位元組會轉成兩個十六進位的數字。 如果 nbytesNone 或未提供,則會使用一個合理的預設值。

>>> token_hex(16)  
'f9bf78b9a18ce6d46a0cd2b0b86df9da'
secrets.token_urlsafe([nbytes=None])

回傳一個 URL 安全的隨機文本字串,包含 nbytes 個隨機位元組。 文本將使用 Base64 編碼,因此平均來說每個位元組會對應到約 1.3 個字元。 如果 nbytesNone 或未提供,則會使用一個合理的預設值。

>>> token_urlsafe(16)  
'Drmhze6EPcv0fN_81Bj-nA'

權杖應當使用多少個位元組?

為了在面對暴力攻擊時能保證安全,權杖必須具有足夠的隨機性。 不幸的是,對隨機性是否足夠的標準,會隨著電腦越來越強大並能夠在更短時間內進行更多猜測而不斷提高。 在 2015 年時,人們認為 32 位元組(256 位元)的隨機性對於 secrets 模組所預期的一般使用場景來說是足夠的。

對於想自行管理權杖長度的使用者,你可以對各種 token_* 函式明白地指定 int 引數(argument)來指定權杖要使用的隨機性程度。 該引數以位元組數來表示要使用的隨機性程度。

否則,如未提供引數,或者如果引數為 None,則 token_* 函式則會使用一個合理的預設值。

備註

該預設值可能在任何時候被改變,包括在維護版本更新的時候。

其他函式

secrets.compare_digest(a, b)

如果字串或類位元組串物件 ab 相等則回傳 True,否則回傳 False,以"恆定時間比較 (constant-time compare) "的處理方式可降低時序攻擊的風險。 請參閱 hmac.compare_digest() 以了解更多細節。

應用技巧和典範實務(best practices)

本節展示了一些使用 secrets 來管理基本安全等級的應用技巧和典範實務。

產生八個字元長的字母數字密碼:

import string
import secrets
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(8))

備註

應用程式不能以可復原的格式存儲密碼,無論是用純文本還是經過加密。 它們應當先加鹽(salt),再使用高加密強度的單向(不可逆)雜湊函式來產生雜湊值。

產生十個字元長的字母數字密碼,其中包含至少一個小寫字母,至少一個大寫字母以及至少三個數字:

import string
import secrets
alphabet = string.ascii_letters + string.digits
while True:
    password = ''.join(secrets.choice(alphabet) for i in range(10))
    if (any(c.islower() for c in password)
            and any(c.isupper() for c in password)
            and sum(c.isdigit() for c in password) >= 3):
        break

產生 XKCD 風格的 passphrase:

import secrets
# On standard Linux systems, use a convenient dictionary file.
# Other platforms may need to provide their own word-list.
with open('/usr/share/dict/words') as f:
    words = [word.strip() for word in f]
    password = ' '.join(secrets.choice(words) for i in range(4))

產生難以猜測的暫時性 URL,內含回復密碼時所用的一個安全性權杖:

import secrets
url = 'https://example.com/reset=' + secrets.token_urlsafe()