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

时间:2012-11-21 00:52:03

标签: python hash random cryptography

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

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

修改

为了更清楚,我正在努力实现将上传到存储的文件名最简单的混淆。

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

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

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

7 个答案:

答案 0 :(得分:39)

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

import uuid

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

输出:

ef21b9ad

答案 1 :(得分: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实际上并不提供它。

答案 2 :(得分: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

答案 3 :(得分:1)

在Python 3.6中,您可能应该使用secrets模块。 secrets.token_urlsafe()似乎适合您的情况,并且可以保证使用加密安全的随机源。

答案 4 :(得分:0)

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

这样做的缺点是如果创建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

答案 5 :(得分:0)

您可以尝试使用shortuuid库。

使用以下命令安装:pip install shortuuid

然后就这么简单:

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

答案 6 :(得分:0)

哪种方法的碰撞更少,更快速,更易于阅读?

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)

样品测试运行

单次运行的结果是从集合abcdefghijklmnopqrstuvwxyz0123456789中抽取1000万个8字符uuid。随机选择与截断的uuid4:

  • 冲突:17-11632
  • 时间(秒):37-63