PHP 7.4 和 MySQL:caching_sha2_password 拒绝长 (20c) 密码

Min*_*ing 7 php mysql authentication mysqli php-7.4

更新:

23.02.2020:该错误已在PHP 7.4.3 中修复。

23.12.2019:我发现了哪些密码受到影响。看我下面的回答。仍然非常感谢为什么拒绝长密码的答案。


免责声明:我已经尝试了大约 2 个小时,只是设置了不同的密码。我绝对相信下面的密码会导致问题。它适用于其他密码。我知道如何修复它:使用不同的密码。我想知道为什么它不起作用。因为这种不一致是不可接受的。

我能够在我的系统上重现该问题。


最近切换到 PHP 7.4 和 MySQL 8,默认情况下使用caching_sha2_password. 在验证它实际上受 PHP 支持后,我无法使用随机生成的密码使其工作,因此我mysql_native_password再次使用 PHP 7.3 。

现在,我设置了一个新服务器并且它工作正常,所以我试图找出问题所在,以便我可以caching_sha2_password在我的其他服务器上使用。

MySQL(通过 mysql shell 使用root

ALTER USER 'test'@'localhost' IDENTIFIED WITH caching_sha2_password BY '';
FLUSH PRIVILEGES;
QUIT
Run Code Online (Sandbox Code Playgroud)

PHP

const DB_HOST = '127.0.0.1';
const DB_USERNAME = 'test';
const DB_PASSWORD = '';
const DB_NAME = 'test_db';
$mysqli = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);
Run Code Online (Sandbox Code Playgroud)

测试(重现)

php db.php
Run Code Online (Sandbox Code Playgroud)

错误

PHP 警告: mysqli::__construct(): (HY000/1045): Access denied for user 'test'@'localhost' (using password: YES) in /db.php on line 5

变通方法:有趣的是,由于某些疯狂的原因,运行此程序再次修复它:

mysql -u test -p
  # Enter password
  QUIT
Run Code Online (Sandbox Code Playgroud)

重新设置密码再次引入问题。

我注意到根据我使用的密码它失败了。所以我认为这可能是不受支持的字符或粘贴问题。

我将我的原始密码 (40chars) 归结为这个 (20chars):

这个密码打破了它: l0QDEptp*L6tNo28ey^8

所有较短的组合都可以正常工作:例如,此密码有效:l0QDEptp*L6tNo28ey^以及此密码:0QDEptp*L6tNo28ey^8

重要提示:仅在安全环境中测试此公共密码!

如果您能够重现该问题或对此有任何想法(解决方案/可能的原因),请告诉我。

注意:这个问题实际上发生在我所有随机生成的密码中,密码长度为 40 个字符并包含特殊字符(例如:%^$!)。我想一些相对常见的组合会触发这个问题。

图片: 演示MySQL 8 和 PHP 7.4 身份验证问题


MySQL 版本: mysql Ver 8.0.18-0ubuntu0.19.10.1 for Linux on aarch64 ((Ubuntu))
PHP 版本: PHP 7.4.1 (cli) (built: Dec 18 2019 14:44:55) ( NTS )
PHP Bug报告: 此处
MySQL 错误报告: 此处

Min*_*ing 1

仅当密码超过 19 个字符时才会出现此问题。可以通过 MySQL Shell ( ) 登录一次来解决此问题mysql -u test -p

PHP测试脚本:

<?php
    # Static
    const DB_HOST = '127.0.0.1';
    const DB_USER = 'test';
    const DB_NAME = 'test_db';
    const INITIAL_PW = 'Test123+++!!!';

    # Variable
    const CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-!^$?#%';
    //const CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    # At least one special char is required, because of my security configuration.
    #  Due to this restriction the script may fail randomly - only 'Access Denied' is important.
    const REQUIRED_SPECIAL_CHAR = '$';
    # Any length below 20 works - 20 and upwards fails.
    const PASSWORD_LENGTH = 20;
    # Number of iterations
    const ITERATIONS = 10;

    # Test
    $pw = INITIAL_PW;
    for($i = 0; $i < ITERATIONS; $i++) {
        $mysqli = new mysqli(DB_HOST, DB_USER, $pw, DB_NAME);
        if($mysqli->connect_errno !== 0) {
            echo('Failed after '.$i.' iterations.'.PHP_EOL);
            echo('Password: '.$pw.PHP_EOL);
            exit;
        }
        $pw = getPassword(PASSWORD_LENGTH);
        $mysqli->query("ALTER USER 'test'@'localhost' IDENTIFIED WITH caching_sha2_password BY '$pw'") OR DIE('Failed to set password (after '.$i.' iterations): '.$mysqli->error.PHP_EOL);
        $mysqli->query('FLUSH PRIVILEGES') OR DIE('Failed to flush: '.$mysqli->error.PHP_EOL);
        $mysqli->close() OR DIE('Failed to close: '.$mysqli->error.PHP_EOL);
    }
    echo('Done.'.PHP_EOL);

    # Helper
    function getPassword(int $length) {
        $pw = '';
        for($i = 1; $i < $length; $i++)
            $pw .= CHARS[rand(0, strlen(CHARS) - 1)];
        $pw .= REQUIRED_SPECIAL_CHAR;
        return $pw;
    }
?>
Run Code Online (Sandbox Code Playgroud)

为什么长密码会发生这种情况?IDK(也许是缓存限制?)