使用哈希作为唯一标识符是否不正确?

6

我希望在一个数据库表中使用PHP生成的唯一ID,该表很可能永远不会超过10000条记录。我不希望创建时间可见或者使用纯数字值,因此我使用了如下方式:

sha1(uniqid(mt_rand(), true))

使用散列作为唯一标识符是否有问题?所有散列是否都会出现冲突,或者概率如此之小以至于在这种情况下不应考虑?

另外一个问题:如果要被散列的字符数量小于sha1哈希中的字符数量,那么它不总是唯一的吗?


3
不,不错。将时间戳传递给SHA1会更好。 - wayne
1
这可能是一篇不错的阅读:https://dev59.com/8E3Sa4cB1Zd3GeqPwZZ3 - Sudhir Bastakoti
为什么要创建自己独特的ID?你的数据库不支持自动增量主键吗?通常认为这是在数据库标识符上拥有唯一ID的方法。 - Patrick M
@Patrick M 我不想让这些 ID 有任何意义。 - texelate
@texelate 这是一个非常奇怪的要求 :-) 跟踪插入顺序是否真的很糟糕呢?我理解不想让结构或代码隐含意义,但主键真的很常见;任何使用关系数据库系统的人都应该知道它的意义。随机 ID 有两个主要缺点:1)它会在不可预测的时间间隔内碰撞,你必须处理它(而主键将在确切、已知的点上溢出)。2)使用无意义的键只会增加系统的复杂性,没有任何好处。使用确定性哈希键比随机哈希键更好。 - Patrick M
1
这样做的一个原因是为了防止黑客在您的应用程序中找到http GET端点,然后仅使用这些顺序自动递增的密钥在您的数据库中漫步。几年前,AT&T就发生了这种情况,很抱歉,找不到相关链接,但我每次设计系统时都会记得它。因此,将端点/customer/5转换为/customer/dhftrt5sahjgertgjwygad是明显的优势-否则被如此简单地黑客攻击将是非常尴尬的事情。 - saswanb
5个回答

8
如果您有两个密钥,理论上最好的情况是1/2 ^ X的碰撞概率,其中X是哈希算法中的位数。这是“最好的情况”,因为输入通常是ASCII,不使用完整字符集,并且哈希函数不会完美地分配,因此在现实生活中,它们会比理论最大值更经常发生碰撞。
回答您的最后一个问题:
进一步的一点:如果要散列的字符数少于sha1哈希中的字符数,那么它不总是唯一的吗?
是的,这是正确的-有点像。但您将面临另一个生成该大小唯一密钥的问题。最简单的方法通常是校验和,因此只需选择足够大的摘要,使碰撞空间足够小以适应您的需求。
正如@wayne建议的那样,一种流行的方法是将连接到您的随机盐(并使用提高熵)。

2
谢谢,那就是:sha1(base64_encode(microtime()。 uniqid(mt_rand(),true))) - texelate
1
@texelate 那将是一个不错的起点。有关盐值的更多信息,请参见此页面,特别是1+2点:http://blog.ircmaxell.com/2012/12/seven-ways-to-screw-up-bcrypt.html - Morten Jensen
谢谢。我需要这个来保证唯一性,而不是安全性,但无论如何都是非常有用的阅读材料。 - texelate

3

如果两个相同,那将是多么可怕?墨菲定律适用 - 如果一百万分之一,甚至是十万分之一的机会可以接受,那就去做吧!实际机会要小得多 - 但如果发生这种情况会导致系统崩溃,那么必须先解决设计缺陷,然后自信地继续。

以下是一个关于概率的问题/答案:SHA1碰撞的概率


3
使用 sha1(time()) 替代,则可以消除重复哈希的随机可能性,只要时间表示的长度短于 sha1 哈希值。 (很可能比您找到一个可工作的 php 解析器要长 ;))

6
如果您每秒只创建了一个对象,那么您可以消除哈希重复的随机可能性,但这并不多见。我建议不要仅使用时间戳作为唯一键的输入。 - Morten Jensen

2

电脑随机数并非真正的随机,你知道吗? 假设你在Unix环境下,唯一可以获取真正随机数的方式是使用 /dev/random,但这是一个阻塞操作,取决于用户的交互行为,例如移动鼠标或键盘输入。从 /dev/urandom 读取可能不太安全,但它比仅使用ASCII字符更好,并且可以立即响应。


0

sha1($ipAddress.time()) 的作用是防止任何人在同一时间使用相同的IP地址。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接