7

我正在使用一个名称为 21 char max 的 api 来表示一个内部会话,该会话的生命周期约为“两天”。我希望使用某种 hasing 的名称没有意义?md5 生成 40 个字符,还有什么我可以使用的吗?

现在我使用 'userid[:10]' + 创建时间:ddhhmmss + 随机 3 个字符。

谢谢,

4

5 回答 5

23

如果我正确阅读了您的问题,您希望生成一些最多 21 个字符的任意标识符令牌。它是否需要高度抗猜测?您给出的示例不是“密码学强”,因为它可以通过搜索不到整个可能键空间的 1/2 来猜测。

您没有说字符是否可以全部是 256 个 ASCII 字符,或者是否需要限制为可打印的 ASCII(包括 33-127)或更小的范围。

有一个为UUID(通用唯一标识符)设计的 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 大小,所以我认为这更多是理论上的问题。我不会担心人们对您的系统进行暴力攻击。

编辑

PS: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 模块。

于 2009-03-07T12:26:47.063 回答
4

MD5 的十六进制表示具有非常差的随机性:每个字符只能获得 4 位熵。

使用随机字符,例如:

import random
import string
"".join([random.choice(string.ascii_letters + string.digits + ".-")
        for i in xrange(21)])

在选择中放入所有可接受的字符。

如果使用正确,使用真正的哈希函数(如 SHA1)也会得到很好的结果,但增加的复杂性和 CPU 消耗似乎不符合您的需求。你只想要一个随机字符串。

于 2009-03-07T10:45:36.433 回答
2

为什么不从 md5 或 SHA1 哈希中获取前 21 个字符?

于 2009-03-07T10:38:26.450 回答
2

base64 模块可以进行 URL 安全编码。所以,如果需要,而不是

u.bytes.encode("base64")

你可以做

import base64

token = base64.urlsafe_b64encode(u.bytes)

并且,方便地转换回来

u = uuid.UUID(bytes=base64.urlsafe_b64decode(token))
于 2009-10-14T17:03:44.447 回答
0

字符还是字节?如果它需要任意字符串,您可以只使用字节而不用担心扩展为可读字符(无论如何,base64 比十六进制更好)。

如果不使用它的十六进制扩展,MD5 会生成 16 个字符。SHA1 在相同条件下生成 20。

>>> import hashlib
>>> len(hashlib.md5('foobar').digest())
16
>>> len(hashlib.sha1('foobar').digest())
20

之后需要很少的额外字节。

于 2009-03-07T10:54:19.237 回答