生成注册激活密钥时如何防止冲突?

Posted

技术标签:

【中文标题】生成注册激活密钥时如何防止冲突?【英文标题】:How to prevent collisions when generating registration activation keys? 【发布时间】:2011-12-27 09:20:51 【问题描述】:

我正在开发一个大项目的注册系统。

成功注册后,服务器会生成一些激活密钥,将其添加到用户行并通过电子邮件发送给用户。为此目的使用一些密码生成器类。

问题是(我知道这听起来很抽象,但我只是想知道),如何避免重复生成?我的意思是,将来生成器是否有机会创建 db 表中已经存在的激活密钥?我应该在生成密钥后检查重复吗?

【问题讨论】:

除非您使用一些极其复杂的密钥生成器,需要 Top500 超级计算机来生成密钥,否则生成密钥并检查数据库是否之前生成过应该是微不足道的。 当然,您迟早会用完唯一值,但这并不重要,因为这些是初始值并且是随机的,因此非常安全。事实上,我们无法告诉您您的算法产生重复的可能性有多大,因为我们根本不知道。 -- 如果你需要断言一个值不是重复的,你可以测试它的唯一性,或者使用一些随机字符串加上时间戳 【参考方案1】:

我建议使用 PEAR 的 Text_Password package。不要试图重新发明这个***。

不要强制密码是严格唯一的。实际上不太强制执行。考虑一下,如果我尝试将密码设置为 xyzzy 并且网站告诉我我不能,这意味着现在我知道某些帐户正在使用 xyzzy 作为密码。我只需要在所有帐户上尝试该密码,直到找到哪个密码。

不要使用哈希摘要作为生成的密码。您的用户不想输入 32 个字符(或更长)的十六进制字符串。我曾在 2001 年使用 PKI 和 MD5 散列编写安全软件激活密钥包的经验。但是没有人会用它,因为钥匙太长了。

请务必使用哈希摘要和盐来存储密码。请阅读我们无畏的领导者撰写的这篇文章:You're Probably Storing Passwords Incorrectly。

另请参阅我对其他一些与密码相关的问题的回答:

How to generate random password, or temporary URL, for resetting password in Zend Framework? php & mysql compare password What data type to use for hashed password field and what length? How large should my password salt be?

【讨论】:

谢谢您的快速回复,先生。这段代码足够安全吗? md5(uniqid(mt_rand(), true)); 还有一件事,这个密钥 md5(uniqid(mt_rand(), true)); 会有多长时间?我应该为数据库列选择哪种类型和长度? 我错过了什么吗?我不认为他正在生成密码。他正在生成激活密钥。 (不过,Text_Password 仍然可以用于此。) 是的,这不是一个精确的比较,但它们足够相似,我认为密码管理的最佳实践是相关的。【参考方案2】:

试试uniqid()

【讨论】:

uniqid 是个坏主意,因为它根本不是随机的,因此完全可以预测。 如果您使用 $more_entropy 参数,这应该不是问题。 谢谢各位帮忙。这段代码足够安全吗? md5(uniqid(mt_rand(), true)); @simshaun 这可能是对的。我不知道它用作熵源。如果您有可用的高质量熵源(/dev/random),您应该使用它。如果不是md5(uniqid(mt_rand(), true)) 可能需要这样做。 考虑到它是一个激活密钥,我会这么说。另外,请记住,一旦激活,您可以从数据库中清除用户的激活密钥。为什么要使用 md5 哈希?【参考方案3】:

使用具有唯一输入(如用户的唯一用户名)的 SHA 或 Whirlpool 等哈希算法将产生预期冲突率为 0 的哈希。

我不会为此推出自己的算法。如前所述,uniqid()md5() 是有保证的解决方案。

【讨论】:

uniqid 保证在您的 PHP 安装范围内生成唯一的 id,因此理论上,uniqid 更好。然而,在实践中,两者可能大致相同。 edit 我会说代码是“安全的”,但在这种情况下,安全并不是一个真正的因素。它会产生一个独特的结果,但它的安全性取决于你如何使用它。 还有一件事,这个密钥 md5(uniqid(mt_rand(), true)); 会有多长时间?我应该为数据库列选择哪种类型和长度? md5() 哈希是 128 位长。如果您选择使用md5(),则可以使用char(128)。见MD5。 @rockerest:存储 128 位不需要 128 个字符。它只需要 32 个十六进制数字,或 16 个二进制字节。【参考方案4】:

您可以使用时间,但它不会是“随机的”,您必须为其添加一些随机性...

$key = md5(time());

【讨论】:

md5 不是碰撞安全的,您的解决方案实际上会为某些同时注册生成相同的激活密钥。 还有一件事,这个密钥 md5(uniqid(mt_rand(), true)); 会有多长时间?我应该为数据库列选择哪种类型和长度? md5 返回一个 32 长度的字符串,所以 varchar(32) 是你需要的表列【参考方案5】:

使用 GUID 作为您的注册密钥,因为它始终是唯一的并且由系统生成

【讨论】:

以上是关于生成注册激活密钥时如何防止冲突?的主要内容,如果未能解决你的问题,请参考以下文章

微服务注册中心的注册表如何更好的防止读写并发冲突

生成 google-services.json 时 SHA 密钥冲突的问题

如何防止灯具与 django post_save 信号代码冲突?

实体框架检测到冲突的更改。尝试使用相同的密钥插入多个实体时可能会发生这种情况

如何防止vmware克隆server2008R2造成SID冲突

在重新提交许多提交时如何防止许多 git 冲突?