secrets — 生成安全的随机数用于管理秘密

在 3.6 版本加入。

源代码: Lib/secrets.py


secrets 模块用于生成加密强度高的随机数,适用于管理密码、账户认证、安全令牌和相关秘密等数据。

特别是,secrets 模块应优先于 random 模块中默认的伪随机数生成器。后者设计用于建模和仿真,而非安全性或密码学。

参见

PEP 506

随机数

secrets 模块提供了对操作系统提供的最安全的随机性来源的访问。

class secrets.SystemRandom

一个用于使用操作系统提供的最高质量来源生成随机数的类。有关更多详细信息,请参阅 random.SystemRandom

secrets.choice(seq)

从非空序列中返回一个随机选择的元素。

secrets.randbelow(exclusive_upper_bound)

返回范围 [0, exclusive_upper_bound) 内的一个随机整数。

secrets.randbits(k)

返回一个带有 k 个随机位的非负整数。

生成令牌

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 参数来明确指定用于令牌的随机性数量。该参数被视为要使用的随机性字节数。

否则,如果未提供参数,或参数为 Nonetoken_* 函数将使用合理的默认值。

备注

该默认值可能随时更改,包括在维护版本中。

其他函数

secrets.compare_digest(a, b)

如果字符串或 类字节对象 ab 相等,则返回 True,否则返回 False,使用“常数时间比较”来降低 定时攻击 的风险。有关更多详细信息,请参阅 hmac.compare_digest()

秘籍和最佳实践

本节展示了使用 secrets 管理基本安全级别的秘籍和最佳实践。

生成一个八个字符的字母数字密码

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

备注

应用程序不应 以可恢复的格式存储密码,无论是纯文本还是加密的。它们应该使用加密强度高的一次性(不可逆)哈希函数进行加盐和哈希处理。

生成一个十个字符的字母数字密码,至少包含一个小写字符、一个大写字符和三个数字

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 风格的密码短语

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()