Is PHP's password_verify() safe against extremely long passwords (DoS attack)?

前端 未结 1 451
闹比i
闹比i 2021-02-02 12:53

The general attack scenario:

In 2013 Django had a general vulnerability as an attacker could create extremely intense CPU calculations via very large pa

相关标签:
1条回答
  • 2021-02-02 13:05

    The password is limited to 72 characters internally in the crypt algorithm.

    To see why, let's look at crypt()'s source: 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;
            }
    

    The password field is a simple char* field. So there's no length information. All that's passed is a normal pointer.

    So if we follow that through, we'll eventually land at BF_set_key.

    The important part is the loop:

    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 is defined to be 16. So the outer loop will loop 18 times (BF_N + 2).

    The inner loop will loop 4 times. 4 * 18 == 72.

    And there you have it, only 72 characters of the key will be read. No more.

    Note

    Now, there's an interesting side-effect to that algorithm. Because it uses C-Strings (strings terminated by a \0 null byte), it's impossible for it to use anything past \0. So a password that contains a null-byte will lose any entropy past it. Example: http://3v4l.org/Y6onV

    0 讨论(0)
提交回复
热议问题