如果我正确阅读了您的问题,您想生成一些任意标识符令牌,该令牌最多必须为 21 个字符。它是否需要高度抗猜测?您给出的示例不是“密码学强”,因为它可以通过搜索不到整个可能键空间的 1/2 来猜测。
您没有说这些字符是否可以全部是 256 个 ASCII 字符,或者是否需要限制为可打印的 ASCII(33-127,包括在内)或更小的范围。
有一个为UUIDs(通用唯一标识符)设计的 Python 模块。您可能需要 uuid4 来生成随机 UUID,并在可用的情况下使用操作系统支持(在 Linux、Mac、FreeBSD 和可能的其他系统上)。
>>> import uuid
>>> u = uuid.uuid4()
>>> u
UUID('d94303e7-1be4-49ef-92f2-472bc4b4286d')
>>> u.bytes
'\xd9C\x03\xe7\x1b\xe4I\xef\x92\xf2G+\xc4\xb4(m'
>>> len(u.bytes)
16
>>>
16 个随机字节非常难以猜测,如果您只想拥有一个不可猜测的不透明标识符,则无需使用 API 允许的全部 21 个字节。
如果你不能使用这样的原始字节,这可能是个坏主意,因为它更难在日志和其他调试消息中使用,也更难用肉眼比较,然后将字节转换成更易读的东西,比如使用 base-64 编码,结果减少到 21(或其他)字节:
>>> u.bytes.encode("base64")
'2UMD5xvkSe+S8kcrxLQobQ==\n'
>>> len(u.bytes.encode("base64"))
25
>>> u.bytes.encode("base64")[:21]
'2UMD5xvkSe+S8kcrxLQob'
>>>
这为您提供了一个长度为 21 的极高质量随机字符串。
您可能不喜欢可以在 base-64 字符串中的“+”或“/”,因为没有适当的转义可能会干扰 URL。由于您已经考虑使用“随机 3 个字符”,我认为这不是您的担心。如果是,您可以将这些字符替换为其他字符('-' 和 '.' 可能会起作用),或者如果存在则删除它们。
正如其他人指出的那样,您可以使用 .encode("hex") 并获得等效的十六进制,但这只是 4 位随机性/字符 * 最多 21 个字符为您提供 84 位随机性,而不是两倍。每一位都会使您的键空间翻倍,从而使理论搜索空间变得非常小。小 2E24 倍。
即使使用十六进制编码,您的密钥空间大小仍然是 2E24,所以我认为这更多是理论上的问题。我不会担心有人对你的系统进行暴力攻击。
编辑:
P.S.:uuid.uuid4 函数使用 libuuid(如果可用)。它的熵来自 os.urandom(如果可用),否则来自当前时间和本地以太网 MAC 地址。如果 libuuid 不可用,则 uuid.uuid4 函数直接从 os.urandom (如果可用)获取字节,否则它使用 random 模块。 random 模块使用基于 os.urandom (如果可用)的默认种子,否则使用基于当前时间的值。每个函数调用都会进行探测,因此如果您没有 os.urandom,那么开销会比您预期的要大一些。
把消息带回家?如果你知道你有 os.urandom 那么你可以这样做
os.urandom(16).encode("base64")[:21]
但如果您不想担心它的可用性,请使用 uuid 模块。