php - PHP 的 password_verify() 对极长密码(DoS 攻击)是否安全?

标签 php c security hash

一般攻击场景:

2013 年 Django 有一个普遍的漏洞,因为攻击者可以通过非常大的密码创建极其密集的 CPU 计算 [ see the security notice here ].我不确定在没有任何进一步检查的情况下使用 PHP 的 password_verify() 和其他密码散列方法时这是否仍然可行。

PHP 文档说:

Using the PASSWORD_BCRYPT for the algo parameter, will result in the password parameter being truncated to a maximum length of 72 characters.

但是,PHP 的代码可能会说一些不同的东西:

C code behind PHP 5.5.0's password_verify() function但是不直接限制传递的参数(可能在 bcrypt 算法的更深层次上?)。此外,PHP implementation不限制论证。

问题:

password_verify()(以及同一函数集的其他函数)是否容易通过最大化 POST 参数受到 DoS 攻击?还请考虑 POST 上传大小远大于 4MB 的站点范围配置情况。

最佳答案

密码在 crypt 算法内部被限制为 72 个字符。

要了解原因,让我们看看 crypt() 的来源:ext/standard/crypt.c

    } else if (
            salt[0] == '$' &&
            salt[1] == '2' &&
            salt[3] == '$') {
        char output[PHP_MAX_SALT_LEN + 1];

        memset(output, 0, PHP_MAX_SALT_LEN + 1);

        crypt_res = php_crypt_blowfish_rn(password, salt, output, sizeof(output));
        if (!crypt_res) {
            ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
            return NULL;
        } else {
            result = zend_string_init(output, strlen(output), 0);
            ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
            return result;
        }

password 字段是一个简单的 char* 字段。所以没有长度信息。传递的只是一个普通的指针。

因此,如果我们坚持下去,我们最终会到达 BF_set_key .

重要的部分是循环:

for (i = 0; i < BF_N + 2; i++) {
    tmp[0] = tmp[1] = 0;
    for (j = 0; j < 4; j++) {
        tmp[0] <<= 8;
        tmp[0] |= (unsigned char)*ptr; /* correct */
        tmp[1] <<= 8;
        tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */

        if (j)
            sign |= tmp[1] & 0x80;
        if (!*ptr)
            ptr = key;
        else
            ptr++;
    }
    diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */

    expanded[i] = tmp[bug];
    initial[i] = BF_init_state.P[i] ^ tmp[bug];
}

BF_N定义为16,所以外循环会循环18次(BF_N + 2)。

内部循环将循环 4 次。 4 * 18 == 72。

你有它,只有 72 个字符的 key 将被读取。没有了。

注意

现在,该算法有一个有趣的副作用。因为它使用 C 字符串(由 \0 空字节终止的字符串),它不可能使用 \0 之后的任何内容。因此,包含空字节的密码将丢失任何过去的熵。示例:http://3v4l.org/Y6onV

关于php - PHP 的 password_verify() 对极长密码(DoS 攻击)是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28951359/

相关文章:

php - 隐藏一个 div,它是一个特定 wordpress 页面上所有页面的一部分

php - 为什么mySQL数据导出会卡在中间?

c - C-尝试扫描十六进制/十进制/八进制值以检查它们是否等于用户输入

c - 如何在 C 中检查 NULL 指针?

ajax - 在内部网络上提供对我的自托管 Web API 的互联网访问。安全威胁?

java - Java 中的 DES 实现。无法获取明文

javascript - 将 PHP 字符串转换为 javascript 字符串

c - C语言中使用位域反转位?

security - 如何使用 Spring Security 3.0.x 处理 HTTP 403

php - 使用 php7 在 Centos7 上安装 imagick?