我试图计算8个字符的短唯一随机文件名,比方说,数千个文件没有可能的名称冲突。这种方法足够安全吗?
base64.urlsafe_b64encode(hashlib.md5(os.urandom(128)).digest())[:8]
为了更清楚,我试图实现最简单的模糊文件名上传到存储。
我发现,8个字符的字符串,足够随机,是非常有效和简单的方法来存储成千上万的文件,没有可能的冲突,当正确实施时。我不需要保证唯一性,只需要足够高的名称冲突不可能性(仅涉及数千个名称)。
文件存储在并发环境中,因此增加共享计数器是可以实现的,但是很复杂。在数据库中存储计数器效率低下。
我也面临这样的事实:random()在某些情况下会在不同的进程中返回相同的伪随机序列。
有没有理由你不能使用tempfile
来生成名称?
像mkstemp
和NamedTemporaryFile
这样的功能绝对保证给你独特的名字;基于随机字节的任何东西都不会给你这个。
如果由于某种原因你实际上并不想要创建文件(例如,你生成的文件名要在某个远程服务器上使用或者某些东西),那么你就不能完全安全,但mktemp
仍然比随机名称更安全。
或者只是将48位计数器存储在某个“足够全局”的位置,这样您就可以保证在碰撞之前经历完整的名称循环,并且还可以保证知道何时会发生碰撞。
它们比阅读urandom
和做md5
更安全,更简单,更有效率。
如果你确实想要生成随机名称,那么''.join(random.choice(my_charset) for _ in range(8))
也会比你正在做的更简单,也更有效率。甚至urlsafe_b64encode(os.urandom(6))
也像MD5哈希一样随机,更简单,更高效。
加密随机性和/或加密散列函数的唯一好处是避免可预测性。如果这不是你的问题,为什么要付钱呢?如果你确实需要避免可预测性,你几乎肯定需要避免种族和其他更简单的攻击,所以避免mkstemp
或NamedTemporaryFile
是一个非常糟糕的主意。
更不用说,正如Root在评论中指出的那样,如果您需要安全性,MD5实际上并不提供它。
你可以试试这个
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
您可以尝试shortuuid库。
安装时间:pip install shortuuid
然后就是这么简单:
> import shortuuid
> shortuuid.uuid()
'vytxeTZskVKR7C7WgdSP3d'
random.choice()
有点快,碰撞大约减少了3个数量级,但是IMO稍微难以阅读。
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)
单次运行结果,从abcdefghijklmnopqrstuvwxyz0123456789
集中获得1000万8-char uuids。随机选择与截断的uuid4:
我正在使用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