secrets – 生成用於密碼層級的亂數
The
secrets
module is used for generating cryptographically strong random numbers suitable for managing data such as passwords, account authentication, security tokens, and related secrets.
Source code: Lib/secrets.py
secrets
模組的用意,是為了要在 Python 內提供一個適用於密碼層級的亂數產生機制,透過 secrets
我們可以輕鬆的產生密碼強度的亂數以及亂數選擇元素,從而避免使用者使用 random
模組來產生隨機亂數並將之用於密碼上。這個模組目前只在 Python 3.6 以後的版本才有,請各位更新到最新版本來使用。
01. Quickstart Tutorial
產生密碼強度的隨機亂數:
1 2 3 4 5 6 |
>>> import secrets >>> secrets.token_bytes() b‘\x1c\xc7\xd0H\xddY\xdb\x00\xf2\xe0\xd9%\xa2<|T\xcf\xe5\x937\xbc\x15(\xce~p\xca9\xc3u\xed)’ >>> secrets.token_bytes() b‘\x13 \x87\xa2\x95]\n\xec\x80\x16=)\x11}4\x17\x07\xf8\t\x99\x16\xb3\xaf\x97u\x8a\x08uY*\xa8\x83’ >>> |
產生密碼強度的隨機字串
1 2 3 4 5 |
>>> secrets.token_hex() ‘e41210fd028b4fdb8d6d58f9f22a080068afc1478b866d09a7d220c0229031cd’ >>> secrets.token_hex() ’60cc8ed871f0977119b2cee9930bc04edf9861304716b198464dfe2d8283f4f9′ >>> |
產生密碼強度的可用於 URL 的隨機字串:
1 2 3 4 5 |
>>> secrets.token_urlsafe() ‘Pls-bAiy8E3qHxjsPCYaGf7IMZX_7T8Z5gio0p55vnw’ >>> secrets.token_urlsafe() ‘cxVoAJmEWTNRo1KDAgWK29UxQFrTYEYJByn4QMNw27M’ >>> |
以密碼強度的方式隨機選擇一個 Sequence 中的元素:
1 2 3 4 5 6 7 |
>>> import string >>> import secrets >>> secrets.choice(string.ascii_letters) ‘y’ >>> ”.join(secrets.choice(string.ascii_letters) for _ in range(8)) ‘HLheWqrd’ >>> |
可以避免被使用旁通道時間攻擊的字串比較:
1 2 3 4 5 6 7 |
>>> a = secrets.token_hex() >>> b = secrets.token_hex() >>> secrets.compare_digest(a, b) False >>> secrets.compare_digest(a, a) True >>> |
02. HOW-TO Guides
我不是 Python 3.6,可以使用這個模組嗎?
理論上不行,因為在 3.6 之前的 Python 沒有這個模組。請更新到 Python 3.6。
強烈建議更新到 Python 3.6,因為真的很好用。好東西要說三次:使用 Python 3.6、更新到 Python 3.6、請用 Python 3.6。
我們可以打開 Python 3.6 Lib/secrets.py 的原始碼參看,其實就會發現這是一個 wrapper 類型的模組,裏面使用的技巧,都能夠向後相容的。如果你真的不能用 Python 3.6,從原始碼複製你需要片段就可以使用了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
DEFAULT_ENTROPY = 32 # number of bytes to return by default def token_bytes(nbytes=None): “””Return a random byte string containing *nbytes* bytes. If *nbytes* is “None“ or not supplied, a reasonable default is used. >>> token_bytes(16) #doctest:+SKIP b’\\xebr\\x17D*t\\xae\\xd4\\xe3S\\xb6\\xe2\\xebP1\\x8b’ “”” if nbytes is None: nbytes = DEFAULT_ENTROPY return os.urandom(nbytes) def token_hex(nbytes=None): “””Return a random text string, in hexadecimal. The string has *nbytes* random bytes, each byte converted to two hex digits. If *nbytes* is “None“ or not supplied, a reasonable default is used. >>> token_hex(16) #doctest:+SKIP ‘f9bf78b9a18ce6d46a0cd2b0b86df9da’ “”” return binascii.hexlify(token_bytes(nbytes)).decode(‘ascii’) def token_urlsafe(nbytes=None): “””Return a random URL-safe text string, in Base64 encoding. The string has *nbytes* random bytes. If *nbytes* is “None“ or not supplied, a reasonable default is used. >>> token_urlsafe(16) #doctest:+SKIP ‘Drmhze6EPcv0fN_81Bj-nA’ “”” tok = token_bytes(nbytes) return base64.urlsafe_b64encode(tok).rstrip(b‘=’).decode(‘ascii’) |
如何產生含有至少3個大寫英文字母、剛好有兩個數字、有小寫字母、且長度為 10 的密碼?
1 2 3 4 5 6 7 8 9 10 |
import secrets import string alphabet = string.ascii_letters + string.digits while True: password = ”.join(secrets.choice(alphabet) for _ in range(10)) if (any(c.islower() for c in password) and sum(c.isupper() for c in password) == 3 and sum(c.isdigit() for c in password) > 2): break |
如何產生字典式密碼?
1 2 3 4 5 6 7 |
alpha beta chris foo bar ... ... |
1 2 3 4 5 |
import secrets with open(‘dict/words) as f: words = [word.strip() for word in f] password = ‘–‘.join(secrets.choice(words) for _ in range(4)) |
如何產生很難猜測的臨時 URL ?
1 2 3 4 |
>>> import secrets >>> token = secrets.token_urlsafe() >>> url = f‘https://www.mysuperwebsite.com.tw/reset={token}’ ‘https://www.mysuperwebsite.com.tw/reset=3qvKLbNUCTKMTdiRCynQ5wezvGD22NSpHRRgvIe-PL0’ |
03. Discussions
由 Steven D’Aprano 在 2015 年 9 月 19 日發起的 PEP 506,目的在於提供 Python 一個與一般安全相關的亂數標準函式庫。發起的動機是因為考量到透過 Python 的標準函式庫,開發者很容易會以錯誤的方式使用亂數,進而引發安全性上的問題。Theo de Raadt,Open BSD 的 founder,就曾經連繫 Guido van Rossum 說明他對於使用 MT (Mersenne Twister,梅森旋轉算法,random
預設使用的隨機演算法) 在機敏資料,例如密碼、sesson key 以及相關事務上的疑慮。
雖然在 random
的官方文件上都有顯式的標記出「出於安全疑慮、請勿在密碼相關的領域上使用 random 模組」,但我們可以確信,這個警告幾乎被許多 Python 使用者忽略、無視、或是誤解。例如說:
- 開發者並沒有閱讀文件,因此沒有看到警告;
- 開發者並不知道自己的用法會造成安全疑慮;
- 開發者沒有意識到問題的發生,他們可能複製了其他網站的程式碼 (或是學到) 錯誤的使用方式。
我們可以從 Google 搜尋驗證這些問題,在 Google 上搜尋 “Python how to generate passwords” 的第一個結果使用了 random
模組。雖然 random 模組並不是設計來使用於網頁程式,但是極有可能被遭到誤用。第二個驗證來自於 Stackoverflow,詢問如何產生密碼的問答中,許多答案使用了 random
,而被接受的答案也是如此。提出使用 random 會有安全疑慮,還會被嗆”你想太多了”。
更多的資訊以及想法可以參考第一個提出想法的 mailing list。
04. References
- 15.3. secrets — Generate secure random numbers for managing secrets
- PEP 506 – Adding A Secrets Module To The Standard Library
Leave a Reply