如何在PHP中生成安全的激活字符串?

19

用户在订阅我的网站之后,我的网站会生成一封电子邮件确认并发送给他们。在邮件内容中,我需要包含激活密钥,类似于:

www.domain.com/activate.php?key=$generatedKey

如何生成密钥?使用sha1($email)吗?

生成密钥后,我将其存储在数据库中,以便用户单击链接时可以通过数据库进行验证。我是新手,请指教。我需要一个电子邮件确认脚本。

6个回答

26

个人而言,我使用一些组合方式,比如:

$generatedKey = sha1(mt_rand(10000,99999).time().$email);

发生碰撞的概率很小,但我建议在发送之前先检查你的数据库(使用唯一约束是一种简单的方式)。


5
如果您将用户ID添加到URL中并检查两者是否匹配,就可以确保没有碰撞的机会。 - Eli
@Eli,有时候一些字符集合会哈希成相同的哈希字符串,尽管这种情况的概率非常小(但仍然存在)。 - alex
3
嗯,我不喜欢向用户透露主键信息。但那是个好主意,Eli。所以主键=$generatedKey&id=$uid。 - St. John Johnson
很好..用户账户激活后,我应该删除激活密钥吗?如果用户想要取消订阅,怎么办? - show_lol
是的,我会设置使激活密钥仅起作用一次。 如果您需要另一个,请生成一个新的。 Alex,我不认为这在这里相关 - 我们只是将哈希用作随机字符生成器。 - Eli
将用户的电子邮件包含在字符串中会使密钥变得不够随机/安全,您觉得呢?此外,我建议存储生成代码的时间,以便在一定时间后可以使其过期。 - twiz

8
你基本上有几个选择:
1)创建一个看似随机的唯一标识符并将其与对应的用户名存储在数据库中。
2)生成一个随机密码,并在链接中包含用户ID和密码,并将密码存储在数据库中。
3)使用单向哈希函数(如md5、sah1等)和一个秘密标识符来加密用户标识符。你不必在数据库中存储加密后的用户标识符。
选项1很困难,因为你必须担心检查数据库以查看键是否已经存在。但是,URL不包含被激活的用户名很好。
如果你将来已经要使用某种数据库来存储用户信息(至少是密码),那么你可以选择选项2。向数据库添加另一个列不需要太多工作量。在发送电子邮件时,将用户名和类似于$key = sha1(rand(1, 99999) . $username)的内容保存到包含用户名的行的另一列中。然后让你的链接看起来像这样:http://you.com/activation.php?user=$username&key=$key。在activation.php中,你检查键是否等于存储在数据库中的值。
如果你想在数据库中使用更少的存储空间,则选项3将起作用。你可以使用类似于$key = sha1($mysecret . $username)的内容作为秘密标识符。使用一些只有你知道的奇怪的东西,比如'aaafj_my_secret_adfaf'作为$mysecret。像选项2中那样使用相同类型的URL。但是,由于你可以仅基于$username生成$key,所以你不需要存储它。因此,在处理activation.php时,只需检查sha1($mysecret . $_GET[username]) == $_GET[key]是否成立。如果是,就知道你有正确的用户。理论上,随着足够多的注册,某人可能会找到你的$mysecret值并生成激活密钥。但是,在他们能够开始计算它之前,你肯定会注意到数十亿或更多的注册。所需的激活次数基于哈希函数的密钥大小。使用sha1(160位)而不是md5(128位)使猜测你的$mysecret值变得更加困难。

1
非常好的选项总结及其优缺点!我只想补充一点,无论您选择哪个选项,都有一些“不变量”适用于所有选项。例如:1.激活链接必须是唯一的,因为如果不是,您无法确定要激活哪个用户。2.您必须在数据库中存储至少电子邮件地址,并且它也必须是唯一的。从这个角度来看,第一个选项的缺点并不是那么大的缺点,因为您最终会检查唯一性(至少是电子邮件地址的唯一性)。 - SasQ

1
$guid=md5(uniqid(mt_rand(), true));

1
如果您将激活字符串存储在数据库中并稍后进行检查,根本不需要哈希值!
您只需要一个长的、随机的字符串。您可以以任何方式生成它,只要确保它足够长即可。实际上,最好与电子邮件或用户名完全无关。

1
这就是为什么 sha1、md5 和其他加密函数很好,因为它们创建了看似随机的字符串。如果你想要独一无二的字符串(低碰撞率),最好的方法是基于某些唯一的常量(例如邮箱)和一些额外的盐值来生成。 - St. John Johnson
2
哈希函数不会增加任何熵,因此在这种情况下它们不会使碰撞更或更少可能发生。它们只是对我们人类来说看起来更随机。 - Eli

0
$code = md5($_POST['username'] . microtime()); 

0

这样就可以了,不过你可以加点盐来改善它,例如:

$key = sha1($email . 'doYouLikeSauce');

另一种方法是生成一个随机密码并通过电子邮件发送。


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