PHP的password_verify()是否可以安全地防止超长密码(DoS攻击)?

Sli*_*liq 19 php c security hash

一般攻击情形:

在2013年,Django有一个普遍的漏洞,因为攻击者可以通过非常大的密码创建极其强大的CPU计算[ 请参阅此处的安全声明 ].在使用PHP的password_verify()和其他密码散列方法而不进行任何进一步检查时,我不确定这是否仍然可行.

PHP文档说:

对algo参数使用PASSWORD_BCRYPT将导致密码参数被截断为最大长度为72个字符.

但是,PHP的代码MAYBE说了一些不同的东西:

然而,PHP 5.5.0的password_verify()函数背后C代码并不直接限制传递的参数(可能在bcrypt算法中更深层次?).此外,PHP实现不限制参数.

问题:

password_verify() (同样的功能集和其他功能),通过刷爆了POST参数防范DoS脆弱?还请考虑POST上载大小远大于4MB的站点范围配置情况.

irc*_*ell 23

密码算法内部密码限制为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;
        }
Run Code Online (Sandbox Code Playgroud)

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];
}
Run Code Online (Sandbox Code Playgroud)

BF_N定义为16.因此外循环将循环18次(BF_N + 2).

内循环将循环4次.4*18 == 72.

你有它,只能读取72个字符的密钥.不再.

注意

现在,这个算法有一个有趣的副作用.因为它使用C-Strings(以\0空字节终止的字符串),所以它不可能使用过去的任何东西\0.因此,包含空字节的密码将丢失任何熵.示例:http://3v4l.org/Y6onV