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

谁说我不能喝 提交于 2019-12-23 13:31:06

问题


UPDATE:

I've found out which passwords are affected. See my answer below. An answer to why long passwords are rejected would still be much appreciated.


Disclaimer: I have tried about 2 hours just setting different passwords. I am absolutely confident that the password below causes the issue. It works with other passwords. I do know how to fix it: use a different password. I want to know why it does not work. Because such inconsistency is not acceptable.

I am able to reproduce the issue on my system.


Recently switched to PHP 7.4 and MySQL 8, which by default use caching_sha2_password. After validating that it is in fact supported by PHP, I couldn't get it to work with my randomly generated password, so I used PHP 7.3 with mysql_native_password again.

Now, I setup a new server and it worked, so I tried to find out where the issue lies, so I can use caching_sha2_password on my other server.

MySQL (via mysql shell using 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 (to reproduce)

php db.php

Error

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

WORKAROUND: Funny thing is running this fixes it again for some insane reason:

mysql -u test -p
  # Enter password
  QUIT

Re-setting the password introduces the issue again.

I noticed that it failed based on the password I used. So I thought it might be unsupported characters or pasting issues.

I boiled my original password (40chars) down to this (20chars):

This password breaks it: l0QDEptp*L6tNo28ey^8

All shorter combinations work with no issue: This password works for example: l0QDEptp*L6tNo28ey^ as well as this one: 0QDEptp*L6tNo28ey^8

Important: Only test this public password in safe environments!

If you are able to reproduce the issue or have any thoughts on this (solution / possible reason), let me know.

Note: This issue actually occurs for all my randomly generated passwords, which are 40 characters long and contain special chars (e.g.: %^$!). I guess some relatively common combination triggers this issue.

Image: Demo


MySQL Version: mysql Ver 8.0.18-0ubuntu0.19.10.1 for Linux on aarch64 ((Ubuntu))
PHP version: PHP 7.4.1 (cli) (built: Dec 18 2019 14:44:55) ( NTS )
PHP Bug report: Here
MySQL Bug report: Here


回答1:


Use this script to automatically set random length and content password, then reconnent to mysql server.

$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++;
}

In my environment there's not a single access denied error occurred after iterate 1 million times.




回答2:


The issue only occurs for passwords with more than 19 charaters. It can be worked around, by logging in once via the MySQL Shell (mysql -u test -p).

PHP Test script:

<?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;
    }
?>

Why does this happen with long passwords? IDK (maybe caching limit?)



来源:https://stackoverflow.com/questions/59432704/php-7-4-mysql-caching-sha2-password-denying-long-20c-passwords

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!