生成注册激活密钥时如何防止冲突?
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 信号代码冲突?
实体框架检测到冲突的更改。尝试使用相同的密钥插入多个实体时可能会发生这种情况