MySQL8 中文参考(二十五)(1)https://developer.aliyun.com/article/1566164
8.2.14 分配帐户密码
连接到 MySQL 服务器的客户端所需的凭据可能包括密码。本节描述了如何为 MySQL 帐户分配密码。
MySQL 将凭据存储在mysql
系统数据库中的user
表中。仅允许具有CREATE USER
权限的用户执行分配或修改密码的操作,或者具有mysql
数据库的权限(INSERT
权限用于创建新帐户,UPDATE
权限用于修改现有帐户)。如果启用了read_only
系统变量,则使用诸如CREATE USER
或ALTER USER
之类的帐户修改语句还需要CONNECTION_ADMIN
权限(或已弃用的SUPER
权限)。
这里的讨论仅总结了最常见的密码分配语句的语法。有关其他可能性的完整详细信息,请参见第 15.7.1.3 节,“CREATE USER 语句”,第 15.7.1.1 节,“ALTER USER 语句”和第 15.7.1.10 节,“SET PASSWORD 语句”。
MySQL 使用插件执行客户端身份验证;请参见第 8.2.17 节,“可插拔身份验证”。在分配密码的语句中,与帐户关联的身份验证插件执行指定的明文密码所需的任何哈希处理。这使得 MySQL 能够在将密码存储在mysql.user
系统表中之前对其进行混淆。对于这里描述的语句,MySQL 会自动对指定的密码进行哈希处理。还有用于CREATE USER
和ALTER USER
的语法,允许直接指定哈希值。有关详细信息,请参阅这些语句的描述。
在创建新帐户时分配密码,请使用CREATE USER
并包含IDENTIFIED BY
子句:
CREATE USER 'jeffrey'@'localhost' IDENTIFIED BY '*password*';
CREATE USER
还支持指定帐户身份验证插件的语法。请参见第 15.7.1.3 节,“CREATE USER 语句”。
要为现有帐户分配或更改密码,请使用带有IDENTIFIED BY
子句的ALTER USER
语句:
ALTER USER 'jeffrey'@'localhost' IDENTIFIED BY '*password*';
如果您未连接为匿名用户,可以在不明确命名自己的帐户的情况下更改自己的密码:
ALTER USER USER() IDENTIFIED BY '*password*';
要从命令行更改帐户密码,请使用mysqladmin命令:
mysqladmin -u *user_name* -h *host_name* password "*password*"
此命令设置密码的帐户是mysql.user
系统表中具有与User
列中的*user_name
和Host
列中的从中连接的客户端主机*匹配的行的帐户。
警告
使用mysqladmin设置密码应被视为不安全。在某些系统上,您的密码会对系统状态程序(如ps)可见,其他用户可以调用这些程序来显示命令行。MySQL 客户端通常在初始化序列期间用零覆盖命令行密码参数。然而,在这个值可见的瞬间仍然存在。此外,在某些系统上,这种覆盖策略是无效的,密码仍然对ps可见。(SystemV Unix 系统和其他系统可能存在这个问题。)
如果您正在使用 MySQL 复制,请注意,目前,作为CHANGE REPLICATION SOURCE TO
语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO
语句(在 MySQL 8.0.23 之前)的一部分使用的密码实际上被限制为 32 个字符的长度;如果密码更长,任何多余的字符都将被截断。这不是由 MySQL Server 普遍强加的任何限制,而是 MySQL 复制特定的问题。
8.2.15 密码管理
MySQL 支持这些密码管理功能:
- 密码过期,要求定期更改密码。
- 密码重用限制,防止选择旧密码。
- 密码验证,要求密码更改时也需指定要替换的当前密码。
- 双密码,使客户端能够使用主密码或辅助密码连接。
- 密码强度评估,要求使用强密码。
- 随机密码生成,作为要求明确管理员指定文字密码的替代方案。
- 密码失败跟踪,以在连续太多次密码错误登录失败后启用临时帐户锁定。
以下各节描述了这些功能,除了密码强度评估,该功能使用validate_password
组件实现,并在第 8.4.3 节,“密码验证组件”中进行了描述。
- 内部与外部凭据存储
- 密码过期策略
- 密码重用策略
- 密码验证要求策略
- 双密码支持
- 随机密码生成
- 登录失败跟踪和临时帐户锁定
重要
MySQL 使用mysql
系统数据库中的表来实现密码管理功能。如果您从早期版本升级 MySQL,则您的系统表可能不是最新的。在这种情况下,服务器在启动过程中写入类似以下的错误日志消息(确切的数字可能有所不同):
[ERROR] Column count of mysql.user is wrong. Expected 49, found 47\. The table is probably corrupted [Warning] ACL table mysql.password_history missing. Some operations may fail.
要纠正此问题,请执行 MySQL 升级过程。请参阅第三章,升级 MySQL。在此之前,无法更改密码。
内部与外部凭据存储
一些认证插件将帐户凭据存储在 MySQL 内部,存储在mysql.user
系统表中:
mysql_native_password
caching_sha2_password
sha256_password
本节中的大部分讨论适用于这些认证插件,因为这里描述的大多数密码管理功能都是基于 MySQL 本身处理的内部凭据存储。其他认证插件将账户凭据存储在 MySQL 之外。对于使用针对外部凭据系统执行认证的插件的账户,密码管理也必须在该系统外部处理。
例外情况是,对于所有账户,而不仅仅是使用内部凭据存储的账户,失败登录跟踪和临时账户锁定选项都适用,因为 MySQL 能够评估任何账户的登录尝试状态,无论它使用内部还是外部凭据存储。
有关各个认证插件的信息,请参见第 8.4.1 节,“认证插件”。
密码过期策略
MySQL 使数据库管理员能够手动使账户密码过期,并建立自动密码过期策略。过期策略可以在全局范围内建立,并且可以设置个别账户要么遵循全局策略,要么使用特定的每个账户行为覆盖全局策略。
要手动使账户密码过期,请使用ALTER USER
语句:
ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE;
此操作会在mysql.user
系统表中的相应行中标记密码过期。
根据策略,密码过期是自动的,并且基于密码的年龄,对于给定账户,从其最近更改密码的日期和时间进行评估。mysql.user
系统表指示每个账户的密码上次更改的时间,并且如果其年龄大于其允许的寿命,则服务器会在客户端连接时自动将密码视为过期。这可以在没有明确手动密码过期的情况下工作。
要在全局范围内建立自动密码过期策略,请使用default_password_lifetime
系统变量。其默认值为 0,这将禁用自动密码过期。如果default_password_lifetime
的值是正整数*N
,则表示允许的密码寿命,即密码必须每N
*天更改一次。
示例:
- 要建立一个全局策略,使密码的寿命约为六个月,请在服务器的
my.cnf
文件中添加以下行:
[mysqld] default_password_lifetime=180
- 要建立一个全局策略,使密码永不过期,请将
default_password_lifetime
设置为 0:
[mysqld] default_password_lifetime=0
default_password_lifetime
也可以在运行时设置和持久化:
SET PERSIST default_password_lifetime = 180; SET PERSIST default_password_lifetime = 0;
SET PERSIST
为正在运行的 MySQL 实例设置一个值。它还保存该值以便在后续服务器重启时继续使用;参见 Section 15.7.6.1, “变量赋值的 SET 语法”。要更改正在运行的 MySQL 实例的值,而不希望其在后续重启时继续使用,使用GLOBAL
关键字而不是PERSIST
。
全局密码过期策略适用于未设置为覆盖它的所有帐户。要为单个帐户建立策略,请使用 CREATE USER
和 ALTER USER
语句的 PASSWORD EXPIRE
选项。参见 Section 15.7.1.3, “CREATE USER 语句”,以及 Section 15.7.1.1, “ALTER USER 语句”。
示例特定帐户语句:
- 要求每 90 天更改一次密码:
CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY; ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;
- 此过期选项会覆盖语句指定的所有帐户的全局策略。
- 禁用密码过期:
CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE NEVER; ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE NEVER;
- 此过期选项会覆盖语句指定的所有帐户的全局策略。
- 延迟到语句指定的所有帐户的全局过期策略:
CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE DEFAULT; ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE DEFAULT;
当客户端成功连接时,服务器会确定帐户密码是否已过期:
- 服务器会检查密码是否已手动过期。
- 否则,服务器会检查密码的年龄是否超过根据自动密码过期策略允许的寿命。如果是,则服务器会将密码视为过期。
如果密码已过期(无论是手动还是自动),服务器要么断开客户端连接,要么限制其允许的操作(参见 Section 8.2.16, “过期密码的服务器处理”)。受限客户端执行的操作会导致错误,直到用户建立新的帐户密码:
mysql> SELECT 1; ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement. mysql> ALTER USER USER() IDENTIFIED BY '*password*'; Query OK, 0 rows affected (0.01 sec) mysql> SELECT 1; +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec)
客户端重置密码后,服务器将为该会话以及使用该帐户的后续连接恢复正常访问。管理员用户也可以重置帐户密码,但该帐户的任何现有受限会话仍然受限。在执行语句成功之前,使用该帐户的客户端必须断开连接并重新连接。
注意
尽管可以通过将过期密码设置为当前值来“重置”过期密码,但作为良好策略,最好选择不同的密码。DBA 可以通过建立适当的密码重用策略来强制不重用。参见 密码重用策略。
密码重用策略
MySQL 允许对先前密码的重用施加限制。重用限制可以基于密码更改次数、经过的时间或两者来建立。重用策略可以在全局范围内建立,并且可以设置个别帐户要么遵循全局策略,要么用特定的每个帐户行为覆盖全局策略。
帐户的密码历史包括其过去分配的密码。MySQL 可以限制新密码从此历史中选择:
- 如果一个帐户受到密码更改次数的限制,新密码不能选择最近一定数量的最近密码之一。例如,如果最小密码更改次数设置为 3,新密码不能与最近的任何 3 个密码相同。
- 如果一个帐户受到经过时间限制,新密码不能从历史中选择的密码中选择。例如,如果密码重用间隔设置为 60,新密码必须不是在过去 60 天内先前选择的密码之一。
注意
空密码不计入密码历史,并可随时重新使用。
要在全局范围内建立密码重用策略,请使用password_history
和password_reuse_interval
系统变量。
例子:
- 要禁止重用最后 6 个密码或新于 365 天的密码,请将以下行放入服务器
my.cnf
文件中:
[mysqld] password_history=6 password_reuse_interval=365
- 要在运行时设置和持久化变量,请使用以下语句:
SET PERSIST password_history = 6; SET PERSIST password_reuse_interval = 365;
SET PERSIST
为运行中的 MySQL 实例设置一个值。它还保存该值以在后续服务器重新启动时传递;参见 Section 15.7.6.1, “SET Syntax for Variable Assignment”。要更改运行中的 MySQL 实例的值,而不使其在后续重新启动时传递,请使用GLOBAL
关键字而不是PERSIST
。
全局密码重用策略适用于未被设置为覆盖它的所有帐户。要为个别帐户建立策略,请使用CREATE USER
和ALTER USER
语句的PASSWORD HISTORY
和PASSWORD REUSE INTERVAL
选项。参见 Section 15.7.1.3, “CREATE USER Statement”和 Section 15.7.1.1, “ALTER USER Statement”。
个别帐户语句示例:
- 要求至少更改 5 次密码才允许重用:
CREATE USER 'jeffrey'@'localhost' PASSWORD HISTORY 5; ALTER USER 'jeffrey'@'localhost' PASSWORD HISTORY 5;
- 此历史长度选项将覆盖语句命名的所有帐户的全局策略。
- 要求至少 365 天经过才允许重用:
CREATE USER 'jeffrey'@'localhost' PASSWORD REUSE INTERVAL 365 DAY; ALTER USER 'jeffrey'@'localhost' PASSWORD REUSE INTERVAL 365 DAY;
- 此经过时间的选项覆盖了语句命名的所有账户的全局策略。
- 要结合两种类型的重用限制,一起使用
PASSWORD HISTORY
和PASSWORD REUSE INTERVAL
:
CREATE USER 'jeffrey'@'localhost' PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 365 DAY; ALTER USER 'jeffrey'@'localhost' PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 365 DAY;
- 这些选项覆盖了语句命名的所有账户的全局策略重用限制。
- 对于两种类型的重用限制,应遵循全局策略:
CREATE USER 'jeffrey'@'localhost' PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT; ALTER USER 'jeffrey'@'localhost' PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT;
需要密码验证的策略
从 MySQL 8.0.13 开始,可以要求验证更改账户密码的尝试,方法是指定要替换的当前密码。这使得 DBA 可以防止用户在未证明知道当前密码的情况下更改密码。否则,例如,如果一个用户暂时离开终端会话而没有注销,那么恶意用户可以使用该会话更改原始用户的 MySQL 密码。这可能会产生不幸的后果:
- 原始用户在管理员重置账户密码之前无法访问 MySQL。
- 直到密码重置发生,恶意用户可以使用良性用户更改的凭据访问 MySQL。
可以在全局范围内建立密码验证策略,并且可以设置单独的账户以延迟全局策略或使用特定的每个账户行为覆盖全局策略。
对于每个账户,其mysql.user
行指示是否有一个账户特定设置,要求在尝试更改密码时验证当前密码。该设置由CREATE USER
和ALTER USER
语句的PASSWORD REQUIRE
选项建立:
- 如果账户设置为
PASSWORD REQUIRE CURRENT
,密码更改必须指定当前密码。 - 如果账户设置为
PASSWORD REQUIRE CURRENT OPTIONAL
,密码更改可能需要但不一定需要指定当前密码。 - 如果账户设置为
PASSWORD REQUIRE CURRENT DEFAULT
,password_require_current
系统变量确定账户的验证要求策略:
- 如果
password_require_current
被启用,密码更改必须指定当前密码。 - 如果
password_require_current
被禁用,密码更改可能需要但不一定需要指定当前密码。
换句话说,如果账户设置不是PASSWORD REQUIRE CURRENT DEFAULT
,则账户设置优先于由password_require_current
系统变量建立的全局策略。否则,账户将遵循password_require_current
设置。
默认情况下,密码验证是可选的:password_require_current
已禁用,并且使用无PASSWORD REQUIRE
选项创建的帐户默认为PASSWORD REQUIRE CURRENT DEFAULT
。
以下表格显示了每个帐户设置如何与password_require_current
系统变量值交互,以确定帐户密码验证所需策略。
表 8.10 密码验证策略
每个帐户设置 | password_require_current 系统变量 | 密码更改是否需要当前密码? |
PASSWORD REQUIRE CURRENT |
OFF |
是 |
PASSWORD REQUIRE CURRENT |
ON |
是 |
PASSWORD REQUIRE CURRENT OPTIONAL |
OFF |
否 |
PASSWORD REQUIRE CURRENT OPTIONAL |
ON |
否 |
PASSWORD REQUIRE CURRENT DEFAULT |
OFF |
否 |
PASSWORD REQUIRE CURRENT DEFAULT |
ON |
是 |
注意
特权用户可以在不指定当前密码的情况下更改任何帐户密码,而不受需要验证的策略限制。特权用户是具有全局CREATE USER
权限或mysql
系统数据库的UPDATE
权限的用户。
要在全局范围内建立密码验证策略,请使用password_require_current
系统变量。其默认值为OFF
,因此不需要指定当前密码来更改帐户密码。
示例:
- 要建立一个全局策略,要求密码更改必须指定当前密码,请在服务器
my.cnf
文件中使用以下行启动服务器:
[mysqld] password_require_current=ON
- 要在运行时设置并持久化
password_require_current
,可以使用以下语句之一:
SET PERSIST password_require_current = ON; SET PERSIST password_require_current = OFF;
SET PERSIST
为正在运行的 MySQL 实例设置一个值。它还保存该值以在后续服务器重新启动时继续使用;请参阅第 15.7.6.1 节,“变量赋值的 SET 语法”。要更改正在运行的 MySQL 实例的值,而不使其在后续重新启动时继续使用,使用GLOBAL
关键字而不是PERSIST
。
全局密码验证所需策略适用于未被设置为覆盖它的所有帐户。要为单个帐户建立策略,请使用CREATE USER
和ALTER USER
语句的PASSWORD REQUIRE
选项。请参阅第 15.7.1.3 节,“CREATE USER 语句”和第 15.7.1.1 节,“ALTER USER 语句”。
示例特定帐户语句:
- 要求更改密码时指定当前密码:
CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT; ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT;
- 此验证选项覆盖了语句命名的所有帐户的全局策略。
- 不要求更改密码时指定当前密码(当前密码可以但不必给出):
CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT OPTIONAL; ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT OPTIONAL;
- 此验证选项覆盖了语句命名的所有帐户的全局策略。
- 延迟到语句命名的所有帐户的全局密码验证要求策略:
CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT; ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT;
当用户使用ALTER USER
或SET PASSWORD
语句更改密码时,当前密码的验证起作用。示例使用ALTER USER
,这比使用SET PASSWORD
更可取,但这里描述的原则对两个语句都是相同的。
在更改密码的语句中,REPLACE
子句指定要替换的当前密码。示例:
- 更改当前用户的密码:
ALTER USER USER() IDENTIFIED BY '*auth_string*' REPLACE '*current_auth_string*';
- 更改命名用户的密码:
ALTER USER 'jeffrey'@'localhost' IDENTIFIED BY '*auth_string*' REPLACE '*current_auth_string*';
- 更改命名用户的身份验证插件和密码:
ALTER USER 'jeffrey'@'localhost' IDENTIFIED WITH caching_sha2_password BY '*auth_string*' REPLACE '*current_auth_string*';
REPLACE
子句的工作原理如下:
- 如果需要为帐户更改密码以指定当前密码,则必须提供
REPLACE
,以验证试图进行更改的用户实际上知道当前密码。 - 如果需要为帐户更改密码但不需要指定当前密码,则
REPLACE
是可选的。 - 如果指定了
REPLACE
,则必须指定正确的当前密码,否则将出现错误。即使REPLACE
是可选的也是如此。 - 只有在更改当前用户的帐户密码时才能指定
REPLACE
。(这意味着在刚刚显示的示例中,明确命名jeffrey
帐户的语句将失败,除非当前用户是jeffrey
。)即使特权用户尝试为另一个用户更改密码,也是如此;但是,这样的用户可以更改任何密码而无需指定REPLACE
。 - 为避免将明文密码写入二进制日志,
REPLACE
被省略。
MySQL8 中文参考(二十五)(3)https://developer.aliyun.com/article/1566166