生成加密安全令牌


83

为了生成一个32个字符的令牌来访问我们的API,我们目前使用:

$token = md5(uniqid(mt_rand(), true));

我已经读到,这种方法不是基于密码的安全性,因为它基于系统时钟,这openssl_random_pseudo_bytes将是一个更好的解决方案,因为它很难预测。

如果是这种情况,等效代码将是什么样?

我猜是这样的,但我不知道这是否对...

$token = md5(openssl_random_pseudo_bytes(32));

我应该传递给函数的长度是多少?


4
为什么要md5呢?只需将字节流转换为十六进制:您将从openssl_random_pseudo_bytes()返回32个字节,使用bin2hex()将这些字节中的每个字节呈现为十六进制值,如PHP docs示例所示
Mark Ba​​ker

我只想要32个字符?我该怎么办?

10
md5()生成32个字符串,但其中仅包含128位数据。openssl_random_pseudo_bytes()返回真正的二进制数据,因此具有32 * 8 = 256位随机性。通过在md5中填充32字节的随机字符串,可以有效地减少其唯一性。
Marc B

1
这样就$token = bin2hex(openssl_random_pseudo_bytes(16));足够了吗?或者我需要一个16次迭代的循环,将1作为长度并将16进制附加到字符串上?

将16个字节转换为十六进制的最终解决方案是正确的。但是您不应该真正依靠OpenSSL #1 #2 #3。一旦您使用PHP 7.0+,就真的没有任何借口可以使用它了。尝试使用Open而不是OpenSSL和十六进制编码,Random::alphanumericString($length)它对这16个字符的适应程度约为20亿倍。
Caw

Answers:


187

这是正确的解决方案:

$token = bin2hex(openssl_random_pseudo_bytes(16));

# or in php7
$token = bin2hex(random_bytes(16));

1
我添加了另一个答案,说明如何使用带CryptoLib的openssl_random_pseudo_bytes生成唯一令牌。这使您可以生成具有所有字符的标记,而不是仅使用0-9 AF。
mjsa 2014年

4
PHP 5.x对random_bytes()和random_int()的支持github.com/paragonie/random_compat
MTK

4
您可能还想要这样,bin2hex(random_bytes($length/2))因为每个字节可以有2个十六进制字符,所以$length没有它,字符数将始终是两倍
A Friend

3
@AFriend同意。如果您要创建一个产生一串字符$length数的函数(而不是关心字节大小),则需要传递random_bytes($length/2)
BadHorsie

10

如果要使用openssl_random_pseudo_bytes,最好使用CrytoLib的实现,这将允许您生成所有字母数字字符,将bin2hex粘贴在openssl_random_pseudo_bytes周围只会导致获得AF(大写)和数字。

用放置cryptolib.php文件的位置替换path / to /(可以在GitHub上找到它:https : //github.com/IcyApril/CryptoLib

<?php
  require_once('path/to/cryptolib.php');
  $token = IcyApril\CryptoLib::randomString(16);
?>

有关CryptoLib的完整文档,请访问:https://cryptolib.ju.je/ 。它涵盖了许多其他随机方法,包括级联加密,散列生成和验证。但是如果您需要它,它就在那里。


对于相同数量的字节:openssl_random_pseudo_bytes(16)每个字节可以具有任何值(0-255),而randomString(16)每个字节只能具有az Az 0-9,即每个字节62个组合。Yup randomString在每个字符串长度上都更好,因为bin2hex编码效率低(50%);最好base64_encode(openssl_random_pseudo_bytes(16))用于较短的字符串和确切已知的安全字节数(在这种情况下为16,但根据需要进行更改)
Rodney

1
@Rodney Bear in mind base 64还将产生一个包含的字符串+/并且=人物,所以,如果你想生成一个用户友好的令牌(如API密钥),它的效果并不理想。
BadHorsie

9

如果您具有加密安全的随机数生成器,则无需对它的输出进行哈希处理。实际上,您不想这么做。只需使用

$token  = openssl_random_pseudo_bytes($BYTES,true)

但是$ BYTES是您想要的许多字节数据。MD5具有128位哈希,因此可以使用16个字节。

附带说明一下,您在原始代码中调用的功能都不是加密安全的,大多数功能都非常有害,以至于即使与安全的其他功能结合使用,仅使用其中一个功能也将不安全。MD5存在安全性问题(尽管对于此应用程序可能不相关)。默认情况下,Uniqid不仅不生成加密随机字节(因为它使用系统时钟),而且您传递的添加的熵是使用线性同余生成器组合的,线性生成器不安全。实际上,这可能意味着即使他们不知道服务器时钟的价值,也可以猜出所有访问其中的API密钥。最后,用作额外熵的mt_rand()也不是安全的随机数生成器。


27
openssl_random_pseudo_bytes()的第二个参数应该是通过引用传递的变量。在openssl_random_pseudo_bytes之后,如果openssl能够使用密码学上强的算法,则该变量为true或false。它不是用来告诉openssl使用加密强度高的算法,无论如何它将尝试这样做。
Jonathan Amend 2013年

-1

可靠的密码您只能使用ascii字符a-zA-Z数字0-9进行输入。要做到这一点,最好的方法是仅使用加密安全的方法,例如PHP7中的random_int()random_bytes()。其余函数用作base64_encode()您只能用作支持函数,以使字符串可靠并将其更改为ASCII字符。

mt_rand()不安全并且非常旧。在任何字符串中,您都必须使用random_int()。从二进制字符串开始您应该使用base64_encode()来使二进制字符串可靠或使用bin2hex,但是随后您将字节仅剪切到16个位置(值)。 请参阅我对此功能的实现。它使用所有语言。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.