帮助我理解 pack()、openssl_random_pseudo_bytes() 和 mt_rand() 用于加盐密码

发布于 2024-10-16 17:21:28 字数 1635 浏览 6 评论 0原文

我正在构建一个具有用户群的应用程序,并且我正在保护登录。我对编程(和 PHP)相当陌生,但迄今为止我的努力都指向使用 Crypt() 和 Blowfish 散列盐。

在进一步讨论之前,请允许我指出我目前对 phpass 不感兴趣

在 crypt() 文档中,一位用户最近发布了以下内容:

<?php 
   $salt = substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(), mt_rand()))), 0, 22); 
?>

它旨在用于系统 其中 mt_getrandmax() == 2147483647。

创建的盐将是 128 位 长度,填充到132位,然后 以 22 个 base64 字符表示。 (CRYPT_BLOWFISH 仅使用 128 位 盐,尽管有 132 22 个 base64 字符中的位。如果你 检查 CRYPT_BLOWFISH 输入并 输出,你可以看到它忽略了 输入的最后四位,并设置 它们在输出时为零。)

请注意, 返回的四个 32 位双字 mt_rand() 将始终为零(因为 mt_getrandmax == 2^31),所以只有 124 128 位将是伪随机的。我 发现对我来说可以接受 应用程序。

我测试了我的服务器,确实 mt_getrandmax() 返回 2147483647。我尝试查阅文档以了解上述代码的真正作用 - pack() 代码 N4 用于 32 位字符串 (大端字节顺序??)重复了 4 次...我认为这就是为什么有 4 个 mt_rand() 参数。

我不明白的是他为什么用.替换+以及22个base64字符的目的(并不是我完全理解base64是什么。)

建议我查看 openssl_random_pseudo_bytes() 来生成随机盐,因为我之前查看的方法将其自身限制为 1234567890abcdefghijklmnopqrstuvwxyz

据说5.3.4之前有一个错误导致openssl_random_pseudo_bytes()< /code> 运行速度非常慢,偶尔会导致超时错误。我不确定是否应该尝试将 openssl_random_pseudo_bytes()Crypt() 一起使用,或者使用类似于上述方法的 mt_rand()pack()

我试图更多地理解所有这些元素是如何工作的,以及它们在概念上的作用,而不是仅仅使用一个而不理解它来实现我的目标;我正在尝试学习:P

有人可以帮助我理解这里工作的不同元素,或者至少引导我到一个我可以阅读的知识库吗?我认为最难以理解的部分是理解不同的格式/术语(base64、ascii、hexdec、bit、byte 等),但最终如何实现一个相当安全的盐来与我的密码一起使用。

I'm building an application that will have a user base, and I'm at the point of securing the login. I'm fairly new to programming (and PHP,) but my efforts thus far have pointed to using Crypt() and a Blowfish hashed salt.

Before I go further, let me specify that I am not interested in phpass at this time.

Within the crypt() documentation, a user recently posted this:

<?php 
   $salt = substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(), mt_rand()))), 0, 22); 
?>

It is intended for use on systems
where mt_getrandmax() == 2147483647.

The salt created will be 128 bits in
length, padded to 132 bits and then
expressed in 22 base64 characters.
(CRYPT_BLOWFISH only uses 128 bits for
the salt, even though there are 132
bits in 22 base64 characters. If you
examine the CRYPT_BLOWFISH input and
output, you can see that it ignores
the last four bits on input, and sets
them to zero on output.)

Note that the high-order bits of the
four 32-bit dwords returned by
mt_rand() will always be zero (since
mt_getrandmax == 2^31), so only 124 of
the 128 bits will be pseudorandom. I
found that acceptable for my
application.

I tested my server, and indeed mt_getrandmax() returns 2147483647. I tried poking around the documentation to understand what the above code really does--the pack() code N4 is for a 32-bit string (big endian byte order??) repeated 4 times... which I assume is why there's 4 mt_rand() arguments.

What I don't understand is why he replaces + with . and the purpose of 22 base64 characters (not that I fully understand what base64 is.)

It was recommended that I look into openssl_random_pseudo_bytes() for my random salt generation, as the previous method I was looking at was limiting itself to just 1234567890abcdefghijklmnopqrstuvwxyz.

Supposedly there was a bug pre 5.3.4 causing openssl_random_pseudo_bytes() to run painfully slow, occassionally causing timeout errors. I'm not sure if I should try to use openssl_random_pseudo_bytes() with Crypt() or something like the above method using mt_rand() and pack().

I'm trying to understand more how all these elements work, and what they are doing conceptually--rather than just using one without understanding it to achieve my goal; I'm trying to learn :P

Can someone help me understand the different elements at work here, or at least direct me to a knowledge base where I can read about it? I think the most eluding component is understanding the different formats/terminology (base64, ascii, hexdec, bit, byte, etc.) but also in the end, how to achieve a fairly secure salt for use with my passwords.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

南七夏 2024-10-23 17:21:28

首先我要说的是,从生成的角度来看,盐没有什么特别的。这只是另一个随机字符串。它的特殊之处在于它的使用方式,但不是生成方式。

您的具体问题

  1. 为什么他用 . 替换 +

    我不知道。也许是因为 + 字符可能会与 url 中的空格混淆。但盐不应该出现在 url 中,所以很可能不是这样。

  2. base64/hexdec 有什么作用:

    Base64 将原始字节流(每个字节的值从 0 到 255)转换为 Base 64 表示形式。它的资源很多,所以不值得深入研究。阅读维基百科文章了解更多信息。

    hexdec 将十六进制数 (a-f0-9) 转换为十进制数。它从基数 16 转换为基数 10(只是表示数字的另一种方式)。

  3. 什么是位和字节:

    位是一个信息单位。它有 2 个状态,0 或 1。一个字节是一系列 8 位。所以一个字节可以有 256 种独特的组合。阅读 Wikipedia...

  4. 什么是ascii

    这是一个字符集。它表示单个 8 位字节中的单个可打印字符。再次,我建议阅读 Wikipedia

一般盐

良好的盐生成函数的目标是大熵。这意味着可能的输出数量尽可能多。因此任何方法都应该产生大量结果。

现在,您需要定义盐可接受的字符(因为您需要存储盐来验证哈希值)。最好的盐是全字节数字,而不仅仅是可显示的字符。现在,您将无法在有意义的庄园中显示它,但您不需要显示它。另外,对于存储,您始终可以使用base64_encode它。

接下来,您需要选择盐的大小。盐越大越好。 32 个字符的盐是可以接受的,但 128 个字符的盐更好。盐的大小和每个字符的选项数量将决定可能性的数量。一些常见的组合:

Hex, 32 characters: 2e38 possibilities
Hex, 128 characters: 1e154 possibilities
Full Byte, 32 characters: 1e77 possibilities
Full Byte, 128 characters: 1e308 possibilities

现在,您需要生成盐。关键是根据需要进行尽可能多的随机调用来填充熵。您可以通过几种方式做到这一点:

  • 系统相关(仅适用于 *nix,但最佳熵):

    $f = fopen('/dev/urandom', 'r');
    $seed = fgets($f, $characters); // 请注意,这将始终返回完整字节
    fclose($f);
    
  • 库相关(很好,但需要安装 OpenSSL)

    $seed = openssl_random_pseudo_bytes($characters);
    
  • 后备

    $seed = '';
    for ($i = 0; $i < $characters; $i++) {
        $seed .= chr(mt_rand(0, 255));
    }
    

现在,您需要将其转换为所需的输出格式。

  • 十六进制(a-f0-9):

    $out = '';
    for ($i = 0, $len = strlen($seed); $i < $len; $i++) {
        $num = ord($seed);
        $out .= dechex(floor($num / 16)) 。 dechex($num % 16);
    }
    
  • Base36 (a-z0-9):

    $out = '';
    for ($i = 0, $len = strlen($seed); $i < $len; $i++) {
        $num = ord($seed);
        $out .= base_convert($num, 10, 36);
    }
    
  • Base64 (a-zA-Z0-9+=):

    $out = base64_encode($seed);
    
  • 完整字节:

    没有什么是必要的,因为它已经是这种格式。

Let me start of by saying that there is nothing special about a salt from the standpoint of generation. It's just another random string. It's special in how it's used, but not generated.

Your specific questions

  1. Why does he replace + with .?

    I have no idea. Perhaps it's because the + character may be confused with a space in urls. But a salt should never be in a url, so that's likely not it.

  2. What does base64/hexdec do:

    Base64 converts a raw byte stream (each byte having values from 0 to 255) into a base 64 representation. There are plenty of resources on it, so it's not worth going deep into. Read the wikipedia article for more information.

    hexdec converts a hex number (a-f0-9) into a decimal one. It converts from base 16 to base 10 (just another way of representing numbers).

  3. What is a bit and byte:

    A bit is a single unit of information. It has 2 states, 0 or 1. A Byte is a series of 8 bits. So a byte can have 256 unique combinations. Read Wikipedia...

  4. What is ascii

    It's a character set. It represents a single printable character in a single 8-bit byte. Again, I'd suggest reading Wikipedia.

Salts in General

The goal of a good salt generation function is large entropy. That means that the number of possible outputs is as large as possible. So any method should produce a large set of results.

Now, you need to define what are acceptable characters for the salt (since you'll need to store the salt to verify the hash). The best possible salts are full-byte numbers and not just displayable characters. Now, you won't be able to display this in a meaningful manor, but you don't need to display it. Plus, for storage, you could always use base64_encode it.

Next, you need to choose how big you want the salt to be. The bigger the salt is, the better. A 32 character salt is acceptable, but a 128 character salt is better. The size of the salt, and the number of options per character will dictate the number of possibilities there are. Some common combinations:

Hex, 32 characters: 2e38 possibilities
Hex, 128 characters: 1e154 possibilities
Full Byte, 32 characters: 1e77 possibilities
Full Byte, 128 characters: 1e308 possibilities

Now, you need to generate the salt. The key is to do as many random calls as necessary to fill out the entropy. You can do this a few ways:

  • System Dependent (only works on *nix but best entropy):

    $f = fopen('/dev/urandom', 'r');
    $seed = fgets($f, $characters); // note that this will always return full bytes
    fclose($f);
    
  • Library dependent (good, but requires OpenSSL to be installed)

    $seed = openssl_random_pseudo_bytes($characters);
    
  • fallback

    $seed = '';
    for ($i = 0; $i < $characters; $i++) {
        $seed .= chr(mt_rand(0, 255));
    }
    

Now, you need to convert it into the desired output format.

  • Hex (a-f0-9):

    $out = '';
    for ($i = 0, $len = strlen($seed); $i < $len; $i++) {
        $num = ord($seed);
        $out .= dechex(floor($num / 16)) . dechex($num % 16);
    }
    
  • Base36 (a-z0-9):

    $out = '';
    for ($i = 0, $len = strlen($seed); $i < $len; $i++) {
        $num = ord($seed);
        $out .= base_convert($num, 10, 36);
    }
    
  • Base64 (a-zA-Z0-9+=):

    $out = base64_encode($seed);
    
  • Full Byte:

    Nothing is necessary since it's already in this format.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文