From 0be85cbb9f45d8890a9a055802ea57619cfb41c5 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Fri, 5 Feb 2021 09:04:24 +1100 Subject: [PATCH 1/2] user host recommend - RENAME USER Altering mysql.user tables isn't something users should do. RENAME USER has existed for a long time, use this instead. Also change SpecificDNSorIp because DNS based grants are a horrible idea, fragile, and could be disabled with --skip-name-resolve. closes #536 --- mysqltuner.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 9523590a8..4e78b008b 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1892,16 +1892,16 @@ sub security_recommendations { } @mysqlstatlist = select_array - "SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE HOST='%'"; + "SELECT CONCAT(QUOTE(user), '\@', host) FROM mysql.user WHERE HOST='%'"; if (@mysqlstatlist) { foreach my $line ( sort @mysqlstatlist ) { chomp($line); my $luser = (split /@/, $line)[0]; badprint "User '" . $line. "' does not specify hostname restrictions."; push( @generalrec, - "Restrict Host for '$luser'\@% to $luser\@SpecificDNSorIp" ); + "Restrict Host for $luser\@% to $luser\@LimitedIPRangeOrLocalhost" ); push( @generalrec, - "UPDATE mysql.user SET host ='SpecificDNSorIp' WHERE user='" . $luser. "' AND host ='%'; FLUSH PRIVILEGES;" ); + "RENAME USER $luser\@'%' TO " . $luser. "\@LimitedIPRangeOrLocalhost;" ); } } From e29b76fca10fa0c01a81c15a0df2f33cc5e6f396 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Fri, 5 Feb 2021 09:34:43 +1100 Subject: [PATCH 2/2] empty passwords - use exact user/host quoted --- mysqltuner.pl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 4e78b008b..421688f2b 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1846,14 +1846,14 @@ sub security_recommendations { # Looking for Empty Password if ( mysql_version_ge(10, 4) ) { @mysqlstatlist = select_array -q{SELECT CONCAT(user, '@', host) FROM mysql.global_priv WHERE +q{SELECT CONCAT(QUOTE(user), '@', QUOTE(host)) FROM mysql.global_priv WHERE user != '' AND JSON_CONTAINS(Priv, '"mysql_native_password"', '$.plugin') AND JSON_CONTAINS(Priv, '""', '$.authentication_string') AND NOT JSON_CONTAINS(Priv, 'true', '$.account_locked')}; } else { @mysqlstatlist = select_array -"SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE ($PASS_COLUMN_NAME = '' OR $PASS_COLUMN_NAME IS NULL) +"SELECT CONCAT(QUOTE(user), '\@', QUOTE(host)) FROM mysql.user WHERE ($PASS_COLUMN_NAME = '' OR $PASS_COLUMN_NAME IS NULL) AND user != '' /*!50501 AND plugin NOT IN ('auth_socket', 'unix_socket', 'win_socket', 'auth_pam_compat') */ /*!80000 AND account_locked = 'N' AND password_expired = 'N' */"; @@ -1862,7 +1862,7 @@ sub security_recommendations { foreach my $line ( sort @mysqlstatlist ) { chomp($line); badprint "User '" . $line . "' has no password set."; - push (@generalrec, "Set up a Secure Password for $line user: SET PASSWORD FOR '".(split /@/, $line)[0]."'\@'SpecificDNSorIp' = PASSWORD('secure_password');") + push (@generalrec, "Set up a Secure Password for $line user: SET PASSWORD FOR $line = PASSWORD('secure_password');") } } else { @@ -1882,12 +1882,12 @@ sub security_recommendations { # Looking for User with user/ uppercase /capitalise user as password @mysqlstatlist = select_array -"SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE user != '' AND (CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(user) OR CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(UPPER(user)) OR CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(CONCAT(UPPER(LEFT(User, 1)), SUBSTRING(User, 2, LENGTH(User)))))"; +"SELECT CONCAT(QUOTE(user), '\@', QUOTE(host)) FROM mysql.user WHERE user != '' AND (CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(user) OR CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(UPPER(user)) OR CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(CONCAT(UPPER(LEFT(User, 1)), SUBSTRING(User, 2, LENGTH(User)))))"; if (@mysqlstatlist) { foreach my $line ( sort @mysqlstatlist ) { chomp($line); - badprint "User '" . $line . "' has user name as password."; - push (@generalrec, "Set up a Secure Password for $line user: SET PASSWORD FOR '".(split /@/, $line)[0]."'\@'SpecificDNSorIp' = PASSWORD('secure_password');"); + badprint "User " . $line . " has user name as password."; + push (@generalrec, "Set up a Secure Password for $line user: SET PASSWORD FOR $line = PASSWORD('secure_password');"); } }