为确认电子邮件生成一个不可猜测的令牌

信息安全 哈希 php
2021-08-19 06:41:04

我正在生成一个令牌,以便在单击验证电子邮件中的链接时使用。我计划使用uniqid()但输出将是可预测的,允许攻击者绕过确认电子邮件。我的解决方案是散列它。但这还不够,因为如果我使用的哈希被发现,那么它仍然是可预测的。解决办法是加盐。我不知道该怎么做,因为如果我使用函数来提供变量盐(例如在伪代码中hash(uniqid()+time())),那么哈希的唯一性是否不再得到保证?我应该使用一个常量散列,这样就足够了(例如hash(uniqid()+asd741)

我认为所有答案都遗漏了重要的一点。它必须是独一无二的。如果openssl_random_pseudo_bytes()两次产生相同的数字怎么办?然后一位用户将无法激活他的帐户。人们的反驳论点是它不可能两次产生相同的数字吗?这就是我考虑的uniqid()原因,因为它的输出是独一无二的。

我想我可以同时使用它们并将它们附加在一起。

4个回答

你想要不可猜测的随机性。然后,使用不可猜测的随机性。使用openssl_random_pseudo_bytes()它将插入本地加密强 PRNG不要使用rand()or mt_rand(),因为这些是可以预测的(mt_rand()统计上是好的,但它不适用于有意识的攻击者)。不要妥协,使用好的 PRNG。不要尝试通过抛出散列函数之类的方式自己制作东西;这条路的尽头只有悲伤。

生成 16 个字节,如果需要字符串,则将它们编码为字符串。bin2hex()以十六进制编码;16 个字节变成 32 个字符。base64_encode()Base64编码;16 个字节变成 24 个字符(最后两个是 '=' 符号)。

16 字节是 128 位,这是“安全值”,使冲突完全不可能发生,您无需担心它们。除非你有充分的理由,否则不要低于这个值(即使那样,也不要低于 16 岁)。

确保你有OpenSSL 支持,你永远不会出错这个单线

$token = bin2hex(openssl_random_pseudo_bytes(16));

在 PHP 7 中,您可以使用random_bytes

生成适用于加密使用的任意长度的加密随机字节字符串,例如在生成盐、密钥或初始化向量时。

因此,要生成一个 16 字节的十六进制令牌,您可以:

$token = bin2hex(random_bytes(16));

形式上,不能保证所有数字都是唯一的。但是发生碰撞的可能性很小,所以你最好不要担心被陨石击中。

这是我用来在 PHP 中生成随机令牌的函数:

<?php
function generateToken($length = 24) {
        if(function_exists('openssl_random_pseudo_bytes')) {
            $token = base64_encode(openssl_random_pseudo_bytes($length, $strong));
            if($strong == TRUE)
                return strtr(substr($token, 0, $length), '+/=', '-_,'); //base64 is about 33% longer, so we need to truncate the result
        }

        //fallback to mt_rand if php < 5.3 or no openssl available
        $characters = '0123456789';
        $characters .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/+'; 
        $charactersLength = strlen($characters)-1;
        $token = '';

        //select some random characters
        for ($i = 0; $i < $length; $i++) {
            $token .= $characters[mt_rand(0, $charactersLength)];
        }        

        return $token;
}
?>