足够安全的8个字符的短唯一随机字符串

问题描述 投票:27回答:6

我试图计算8个字符的短唯一随机文件名,比方说,数千个文件没有可能的名称冲突。这种方法足够安全吗?

base64.urlsafe_b64encode(hashlib.md5(os.urandom(128)).digest())[:8]

编辑

为了更清楚,我试图实现最简单的模糊文件名上传到存储。

我发现,8个字符的字符串,足够随机,是非常有效和简单的方法来存储成千上万的文件,没有可能的冲突,当正确实施时。我不需要保证唯一性,只需要足够高的名称冲突不可能性(仅涉及数千个名称)。

文件存储在并发环境中,因此增加共享计数器是可以实现的,但是很复杂。在数据库中存储计数器效率低下。

我也面临这样的事实:random()在某些情况下会在不同的进程中返回相同的伪随机序列。

python hash random cryptography
6个回答
19
投票

有没有理由你不能使用tempfile来生成名称?

mkstempNamedTemporaryFile这样的功能绝对保证给你独特的名字;基于随机字节的任何东西都不会给你这个。

如果由于某种原因你实际上并不想要创建文件(例如,你生成的文件名要在某个远程服务器上使用或者某些东西),那么你就不能完全安全,但mktemp仍然比随机名称更安全。

或者只是将48位计数器存储在某个“足够全局”的位置,这样您就可以保证在碰撞之前经历完整的名称循环,并且还可以保证知道何时会发生碰撞。

它们比阅读urandom和做md5更安全,更简单,更有效率。

如果你确实想要生成随机名称,那么''.join(random.choice(my_charset) for _ in range(8))也会比你正在做的更简单,也更有效率。甚至urlsafe_b64encode(os.urandom(6))也像MD5哈希一样随机,更简单,更高效。

加密随机性和/或加密散列函数的唯一好处是避免可预测性。如果这不是你的问题,为什么要付钱呢?如果你确实需要避免可预测性,你几乎肯定需要避免种族和其他更简单的攻击,所以避免mkstempNamedTemporaryFile是一个非常糟糕的主意。

更不用说,正如Root在评论中指出的那样,如果您需要安全性,MD5实际上并不提供它。


40
投票

您当前的方法应该足够安全,但您也可以查看uuid模块。例如

import uuid

print str(uuid.uuid4())[:8]

输出:

ef21b9ad

1
投票

你可以试试这个

import random
uid_chars = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
             'v', 'w', 'x', 'y', 'z','1','2','3','4','5','6','7','8','9','0')
uid_length=8
def short_uid():
    count=len(uid_chars)-1
    c=''
    for i in range(0,uid_length):
        c+=uid_chars[random.randint(0,count)]
    return c

例如:

print short_uid()
nogbomcv

1
投票

您可以尝试shortuuid库。

安装时间:pip install shortuuid

然后就是这么简单:

> import shortuuid
> shortuuid.uuid()
'vytxeTZskVKR7C7WgdSP3d'

1
投票

Which method has less collisions, is faster and easier to read?

TLDR

random.choice()有点快,碰撞大约减少了3个数量级,但是IMO稍微难以阅读。

Code

import string   
import uuid
import random

def random_choice():
    alphabet = string.ascii_lowercase + string.digits
    return ''.join(random.choices(alphabet, k=8))

def truncated_uuid4():
    return str(uuid.uuid4())[:8]

def test_collisions(fun):
    out = set()
    count = 0
    for _ in range(1000000):
        new = fun()
        if new in out:
            count += 1
        else:
            out.add(new)
    print(count)

test_collisions(random_choice)
test_collisions(truncated_uuid4)

Sample test run

单次运行结果,从abcdefghijklmnopqrstuvwxyz0123456789集中获得1000万8-char uuids。随机选择与截断的uuid4:

  • 碰撞:17 - 11632
  • 时间(秒):37 - 63

0
投票

我正在使用hashids将时间戳转换为唯一ID。 (如果需要,您甚至可以将其转换回时间戳)。

这样做的缺点是如果你创建id太快,你将得到重复。但是,如果您在中间生成它们,那么这是一个选项。

这是一个例子:

from hashids import Hashids
from datetime import datetime
hashids = Hashids(salt = "lorem ipsum dolor sit amet", alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
print(hashids.encode(int(datetime.today().timestamp()))) #'QJW60PJ1' when I ran it
© www.soinside.com 2019 - 2024. All rights reserved.