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

PHP 7.4 & MySQL: caching_sha2_password Denying long (20c) passwords

提问人:Minding 提问时间:12/21/2019 最后编辑:Minding 更新时间:2/24/2020 访问量:2338

问:

更新:

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

23.12.2019:我已发现哪些密码会受到影响。请参阅下面的回答。 对于为什么拒绝长密码的答案仍然不胜感激。


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

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


最近切换到 PHP 7.4 和 MySQL 8,它们默认使用 . 在验证它确实受PHP支持后,我无法让它与我随机生成的密码一起使用,所以我再次使用了PHP 7.3。caching_sha2_passwordmysql_native_password

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

MySQL(通过 mysql shell 使用root)

ALTER USER 'test'@'localhost' IDENTIFIED WITH caching_sha2_password BY '';
FLUSH PRIVILEGES;
QUIT

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);

TEST(重现)

php db.php

错误

PHP 警告:mysqli::__construct():(HY000/1045):第 5 行 /db.php 中的用户“test”@“localhost”(使用密码:YES)的访问被拒绝

解决方法:有趣的是,由于某种疯狂的原因,运行它再次修复了它:

mysql -u test -p
  # Enter password
  QUIT

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

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

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

此密码会破坏它:l0QDEptp*L6tNo28ey^8

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

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

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

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

图片:演示MySQL 8 & PHP 7.4 Authentication issue


MySQL版本:mysql Ver 8.0.18-0ubuntu0.19.10.1 for Linux on aarch64 ((Ubuntu)) PHP版本:PHP 7.4.1(cli)(构建时间:Dec 18 2019 14:44:55) (NTS )

PHP错误报告:这里 MySQL错误报告:这里

php mysql 身份验证 mysqli php-7.4

评论

0赞 Minding 12/21/2019
这让我发疯了。
1赞 Bill Karwin 12/21/2019
我无法重现任何错误。我尝试了空白密码和您所说的长密码在您的环境中中断,但两者都有效。
0赞 Funk Forty Niner 12/21/2019
您是否尝试过而不是 IP 地址?const DB_HOST = 'localhost';
0赞 Minding 12/21/2019
@BillKarwin 它只是没有填写。密码如下。你试过这个确切的密码吗?我试了大约一百次。
1赞 Vincent 2/23/2020
@Minding 已修复 php.net/ChangeLog-7.php#7.3.15

答:

2赞 n0099 12/22/2019 #1

使用此脚本自动设置随机长度和内容密码,然后重新连接到 mysql 服务器。

$mysqli = new mysqli('localhost', 'username', 'initialpassword', 'dbname');
$i=0;
while (true) {
    if($i!=0){
        $mysqli = new mysqli('localhost', 'username', $pw, 'dbname');
    }
    $pw = bin2hex(random_bytes(rand(1, 16)));
    $successd = $mysqli->query("ALTER USER 'username'@'localhost' IDENTIFIED WITH caching_sha2_password BY '{$pw}'");
    echo "times:{$i} pw:{$pw} {$successd}\n";
    $i++;
}

在我的环境中,迭代 100 万次后没有发生任何访问被拒绝错误。

评论

0赞 Minding 12/22/2019
我不确定为什么似乎没有人能够重现这个问题。我在两个不同的服务器上安装了它,并且能够重现它(使用给定的密码)。但是,我没有通过PHP更改密码,而是通过mysql shell更改密码。
0赞 Minding 12/22/2019
您是否尝试了我上面提到的确切密码?
0赞 n0099 12/22/2019
我已经第一次通过mysql-cli尝试了您的密码设置,您也可以在php中使用exec()通过mysql-cli更改密码。我的php版本是PHP 7.4.1(cli)(构建时间:2019年12月18日14:44:22)(NTS),php-mysql客户端api库版本:mysqlnd 7.4.1,mysql版本:8.0.18。
1赞 Minding 12/23/2019
感谢您的测试,我找到了“答案”(何时回答,而不是为什么回答)并将其与测试脚本一起发布。我很好奇你是否可以重现它(如果你不忙的话)。
1赞 n0099 12/23/2019
现在,我可以使用您的脚本或仅使用任何超过 19 个字符的密码(如 12345678901234567890)来获取这些拒绝访问错误,然后在 mysql-cli 中登录一次后可以暂时修复它。
1赞 Minding 12/23/2019 #2

此问题仅发生在具有 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;
    }
?>

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