我试图计算为8个字符的短唯一的随机文件名,比方说,成千上万的文件,而不可能名称冲突。 这是方法足够安全?
base64.urlsafe_b64encode(hashlib.md5(os.urandom(128)).digest())[:8]
编辑
为了更清楚,我想实现简单的文件名可能被混淆上传到存储。
我想通了,8个字符的字符串,足够的随机,将文件数万存储无疑似碰撞,实施正确的时候非常有效和简单的方式。 我并不需要保证唯一性,名称冲突(谈论仅几千名的)的只有足够高的不可能性。
文件被存储在并发环境中,因此增加共用的反是可以实现的,但复杂。 在数据库中存储计数器将是低效的。
我也面临着随机的()在某些情况下返回不同的工艺相同的伪随机序列的事实。
有没有什么不能使用的原因tempfile
生成的名字呢?
像函数mkstemp
和NamedTemporaryFile
绝对保证给你独一无二的名称; 基于随机字节没有什么能比给你的。
如果由于某种原因,你其实不愿意尚未创建文件(例如,您正在生成被一些远程服务器或东西上使用的文件名),你不能是完全安全的,但mktemp
仍比随机名称更安全。
或者只是保持存储在一些“全球性不够”位置的48位计数器,所以你保证碰撞之前通过名称的完整循环下去,你也保证在发生碰撞时会发生什么了解。
他们都是比阅读更安全,更简单,而且更有效urandom
和做的md5
。
如果你确实想要生成随机的名字, ''.join(random.choice(my_charset) for _ in range(8))
也将是比你在做什么更简单,更高效。 即使urlsafe_b64encode(os.urandom(6))
只是随机的MD5哈希值,以及更简单,更高效。
加密随机性和/或密码散列函数的唯一的好处是避免可预测性。 如果这不是你的问题,为什么支付? 如果你确实需要避免可预测性,你几乎肯定需要避免种族和其他更简单的攻击,所以避免mkstemp
或NamedTemporaryFile
是一个非常糟糕的主意。
更何况,作为根在评论中指出,如果你需要的安全,MD5实际上并没有提供它。
您当前的方法应该是足够安全的,但你也可以看看到uuid
模块。 例如
import uuid
print str(uuid.uuid4())[:8]
输出:
ef21b9ad
你可以试试这个
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'
哪种方法具有较少的碰撞,更快,更容易阅读?
TLDR
该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)
样品试运行
在1000万单次运行结果得出的8字符的UUID从集合abcdefghijklmnopqrstuvwxyz0123456789
。 随机选择VS截断uuid4:
- 碰撞:17 - 11632
- 时间(秒):37 - 63
我使用hashids的时间戳转换成一个唯一的ID。 (你甚至可以将其转换回时间戳如果你想)。
这样做的缺点是,如果你创建IDS太快,你会得到一个副本。 但是,如果你用的时间产生在他们之间,那么这是一个选项。
下面是一个例子:
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