MySQL8-中文参考-十二-

MySQL8 中文参考(十二)

原文:docs.oracle.com/javase/tutorial/reallybigindex.html

8.2.9 保留帐户

原文:dev.mysql.com/doc/refman/8.0/en/reserved-accounts.html

MySQL 安装过程的一部分是数据目录初始化(请参阅 Section 2.9.1,“初始化数据目录”)。在数据目录初始化期间,MySQL 创建应被视为保留的用户帐户:

  • 'root'@'localhost: 用于管理目的。此帐户具有所有特权,是系统帐户,并且可以执行任何操作。

    严格来说,此帐户名称并非保留,因为某些安装将root帐户重命名为其他名称,以避免暴露具有众所周知名称的高度特权帐户。

  • 'mysql.sys'@'localhost': 用作sys模式对象的DEFINER。使用mysql.sys帐户可以避免如果 DBA 重命名或删除root帐户而导致的问题。此帐户已锁定,因此不能用于客户端连接。

  • 'mysql.session'@'localhost': 内部使用的帐户,用于插件访问服务器。此帐户已锁定,因此不能用于客户端连接。该帐户是系统帐户。

  • 'mysql.infoschema'@'localhost': 用作INFORMATION_SCHEMA视图的DEFINER。使用mysql.infoschema帐户可以避免如果 DBA 重命名或删除 root 帐户而导致的问题。此帐户已锁定,因此不能用于客户端连接。

8.2.10 使用角色

原文:dev.mysql.com/doc/refman/8.0/en/roles.html

MySQL 角色是一组命名的权限集合。与用户帐户一样,角色可以被授予和撤销权限。

用户帐户可以被授予角色,从而向帐户授予与每个角色相关联的权限。这使得可以将一组权限分配给帐户,并为所需的权限分配提供了一个方便的替代方法,用于概念化所需的权限分配并实施它们。

以下列表总结了 MySQL 提供的角色管理功能:

  • CREATE ROLEDROP ROLE 创建和移除角色。

  • GRANTREVOKE 分配权限以撤销用户帐户和角色的权限。

  • SHOW GRANTS 显示用户帐户和角色的权限和角色分配。

  • SET DEFAULT ROLE 指定默认情况下活动的帐户角色。

  • SET ROLE 更改当前会话中的活动角色。

  • CURRENT_ROLE() 函数显示当前会话中活动的角色。

  • mandatory_rolesactivate_all_roles_on_login 系统变量允许定义强制角色和用户登录到服务器时自动激活授予的角色。

有关单个角色操作语句的描述(包括使用它们所需的权限),请参阅第 15.7.1 节,“帐户管理语句”。以下讨论提供了角色使用的示例。除非另有说明,此处显示的 SQL 语句应使用具有足够管理权限的 MySQL 帐户(如 root 帐户)执行。

  • 创建角色并授予权限

  • 定义强制角色

  • 检查角色权限

  • 激活角色

  • 撤销角色或角色权限

  • 删除角色

  • 用户和角色的可互换性

创建角色并授予权限

考虑以下情景:

  • 一个应用程序使用名为app_db的数据库。

  • 与应用程序关联,可以为创建和维护应用程序的开发人员以及与之交互的用户创建帐户。

  • 开发人员需要对数据库拥有完全访问权限。一些用户只需要读取访问权限,其他用户需要读取/写入访问权限。

为了避免向可能有许多用户帐户单独授予权限,将角色创建为所需权限集的名称。这样可以轻松地向用户帐户授予所需的权限,方法是授予适当的角色。

要创建角色,请使用CREATE ROLE语句:

CREATE ROLE 'app_developer', 'app_read', 'app_write';

角色名称与用户帐户名称非常相似,由'*user_name*'@'*host_name*'格式中的用户部分和主机部分组成。如果省略主机部分,则默认为'%'。用户和主机部分可以不带引号,除非它们包含特殊字符,如-%。与帐户名称不同,角色名称的用户部分不能为空。有关更多信息,请参见第 8.2.5 节“指定角色名称”。

要为角色分配权限,执行与为用户帐户分配权限相同的语法的GRANT语句:

GRANT ALL ON app_db.* TO 'app_developer';
GRANT SELECT ON app_db.* TO 'app_read';
GRANT INSERT, UPDATE, DELETE ON app_db.* TO 'app_write';

现在假设最初您需要一个开发人员帐户,两个需要只读访问权限的用户帐户和一个需要读取/写入访问权限的用户帐户。使用CREATE USER创建这些帐户:

CREATE USER 'dev1'@'localhost' IDENTIFIED BY 'dev1pass';
CREATE USER 'read_user1'@'localhost' IDENTIFIED BY 'read_user1pass';
CREATE USER 'read_user2'@'localhost' IDENTIFIED BY 'read_user2pass';
CREATE USER 'rw_user1'@'localhost' IDENTIFIED BY 'rw_user1pass';

为每个用户帐户分配所需的权限,您可以使用与刚刚显示的相同形式的GRANT语句,但这需要为每个用户枚举单独的权限。相反,使用另一种允许授予角色而不是权限的GRANT语法:

GRANT 'app_developer' TO 'dev1'@'localhost';
GRANT 'app_read' TO 'read_user1'@'localhost', 'read_user2'@'localhost';
GRANT 'app_read', 'app_write' TO 'rw_user1'@'localhost';

对于rw_user1帐户的GRANT语句授予读取和写入角色,这些角色结合起来提供所需的读取和写入权限。

将角色授予帐户的GRANT语法与授予权限的语法不同:有一个ON子句用于分配权限,而没有ON子句用于分配角色。因为语法不同,您不能在同一语句中混合分配权限和角色。(允许向帐户分配权限和角色,但必须使用适用于要授予的内容的语法的单独的GRANT语句。)截至 MySQL 8.0.16,无法向匿名用户授予角色。

创建角色时,角色被锁定,没有密码,并分配默认的身份验证插件。(这些角色属性可以稍后由具有全局CREATE USER权限的用户使用ALTER USER语句更改。)

当角色被锁定时,无法用于服务器身份验证。如果解锁,则可以用于身份验证。这是因为角色和用户都是授权标识符,有很多共同之处,很少有区别。另请参阅用户和角色的可互换性。

定义强制角色

可以通过在mandatory_roles系统变量的值中命名角色来指定角色为强制角色。服务器将强制角色视为授予所有用户的角色,因此不需要显式授予任何帐户。

要在服务器启动时指定强制角色,请在服务器的my.cnf文件中定义mandatory_roles

[mysqld]
mandatory_roles='role1,role2@localhost,r3@%.example.com'

要在运行时设置和持久化mandatory_roles,请使用以下语句:

SET PERSIST mandatory_roles = 'role1,role2@localhost,r3@%.example.com';

SET PERSIST为运行中的 MySQL 实例设置一个值。它还保存该值,导致其在后续服务器重启时保留。要更改运行中的 MySQL 实例的值,而不使其在后续重启时保留,使用GLOBAL关键字而不是PERSIST。请参阅第 15.7.6.1 节,“变量赋值的 SET 语法”。

设置mandatory_roles需要ROLE_ADMIN权限,除了通常需要设置全局系统变量的SYSTEM_VARIABLES_ADMIN权限(或已弃用的SUPER权限)。

强制角色,就像显式授予的角色一样,在激活之前不会生效(请参阅激活角色)。在登录时,如果启用了activate_all_roles_on_login系统变量,则为所有授予的角色激活,否则为设置为默认角色的角色激活。在运行时,SET ROLE激活角色。

mandatory_roles值中命名的角色不能通过REVOKEDROP ROLEDROP USER来撤销。

为防止会话默认成为系统会话,具有SYSTEM_USER权限的角色不能列在mandatory_roles系统变量的值中:

  • 如果在启动时将mandatory_roles分配给具有SYSTEM_USER权限的角色,则服务器会向错误日志写入消息并退出。

  • 如果在运行时将mandatory_roles分配给具有SYSTEM_USER权限的角色,则会发生错误,并且mandatory_roles值保持不变。

即使有此保障,最好避免通过角色授予SYSTEM_USER权限,以防止权限升级的可能性。

如果在mysql.user系统表中不存在mandatory_roles中命名的角色,则不会将该角色授予用户。当服务器尝试为用户激活角色时,它不会将不存在的角色视为强制角色,并将警告写入错误日志。如果稍后创建了角色并因此变为有效,则可能需要使用FLUSH PRIVILEGES使服务器将其视为强制角色。

SHOW GRANTS 根据第 15.7.7.21 节,“SHOW GRANTS Statement”中描述的规则显示强制角色。

检查角色权限

要验证分配给账户的权限,请使用SHOW GRANTS。例如:

mysql> SHOW GRANTS FOR 'dev1'@'localhost';
+-------------------------------------------------+
| Grants for dev1@localhost                       |
+-------------------------------------------------+
| GRANT USAGE ON *.* TO `dev1`@`localhost`        |
| GRANT `app_developer`@`%` TO `dev1`@`localhost` |
+-------------------------------------------------+

然而,这显示了每个授予的角色,而没有将其“展开”为角色代表的权限。要显示角色权限,还需添加一个USING子句,命名要显示权限的授予角色:

mysql> SHOW GRANTS FOR 'dev1'@'localhost' USING 'app_developer';
+----------------------------------------------------------+
| Grants for dev1@localhost                                |
+----------------------------------------------------------+
| GRANT USAGE ON *.* TO `dev1`@`localhost`                 |
| GRANT ALL PRIVILEGES ON `app_db`.* TO `dev1`@`localhost` |
| GRANT `app_developer`@`%` TO `dev1`@`localhost`          |
+----------------------------------------------------------+

类似地验证每种类型的用户:

mysql> SHOW GRANTS FOR 'read_user1'@'localhost' USING 'app_read';
+--------------------------------------------------------+
| Grants for read_user1@localhost                        |
+--------------------------------------------------------+
| GRANT USAGE ON *.* TO `read_user1`@`localhost`         |
| GRANT SELECT ON `app_db`.* TO `read_user1`@`localhost` |
| GRANT `app_read`@`%` TO `read_user1`@`localhost`       |
+--------------------------------------------------------+
mysql> SHOW GRANTS FOR 'rw_user1'@'localhost' USING 'app_read', 'app_write';
+------------------------------------------------------------------------------+
| Grants for rw_user1@localhost                                                |
+------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `rw_user1`@`localhost`                                 |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `app_db`.* TO `rw_user1`@`localhost` |
| GRANT `app_read`@`%`,`app_write`@`%` TO `rw_user1`@`localhost`               |
+------------------------------------------------------------------------------+

SHOW GRANTS 根据第 15.7.7.21 节,“SHOW GRANTS Statement”中描述的规则显示强制角色。

激活角色

授予用户账户的角色可以在账户会话中处于活动或非活动状态。如果授予的角色在会话中处于活动状态,则其权限生效;否则,不生效。要确定当前会话中哪些角色处于活动状态,请使用CURRENT_ROLE()函数。

默认情况下,将角色授予给一个账户或在mandatory_roles系统变量值中命名它不会自动导致角色在账户会话中变为活动状态。例如,因为在前面的讨论中到目前为止没有激活任何rw_user1角色,如果您以rw_user1身份连接到服务器并调用CURRENT_ROLE()函数,结果是NONE(没有活动角色):

mysql> SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE           |
+----------------+

要指定每次用户连接到服务器并进行身份验证时应激活哪些角色,请使用SET DEFAULT ROLE。要将默认设置为早期创建的每个账户的所有分配角色,请使用此语句:

SET DEFAULT ROLE ALL TO
  'dev1'@'localhost',
  'read_user1'@'localhost',
  'read_user2'@'localhost',
  'rw_user1'@'localhost';

现在,如果您以rw_user1身份连接,CURRENT_ROLE()的初始值反映了新的默认角色分配:

mysql> SELECT CURRENT_ROLE();
+--------------------------------+
| CURRENT_ROLE()                 |
+--------------------------------+
| `app_read`@`%`,`app_write`@`%` |
+--------------------------------+

要在用户连接到服务器时自动激活所有明确授予和强制性角色,请启用activate_all_roles_on_login系统变量。默认情况下,自动角色激活被禁用。

在一个会话中,用户可以执行SET ROLE来改变活动角色集。例如,对于rw_user1

mysql> SET ROLE NONE; SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE           |
+----------------+
mysql> SET ROLE ALL EXCEPT 'app_write'; SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| `app_read`@`%` |
+----------------+
mysql> SET ROLE DEFAULT; SELECT CURRENT_ROLE();
+--------------------------------+
| CURRENT_ROLE()                 |
+--------------------------------+
| `app_read`@`%`,`app_write`@`%` |
+--------------------------------+

第一个SET ROLE语句取消所有角色。第二个使rw_user1有效地只读。第三个恢复默认角色。

存储程序和视图对象的有效用户受DEFINERSQL SECURITY属性的影响,这些属性确定执行是在调用者还是定义者上下文中发生(参见第 27.6 节,“存储对象访问控制”):

  • 在调用者上下文中执行的存储程序和视图对象将使用当前会话中处于活动状态的角色执行。

  • 在定义者上下文中执行的存储程序和视图对象将使用其DEFINER属性中命名的用户的默认角色执行。如果启用了activate_all_roles_on_login,这样的对象将使用授予DEFINER用户的所有角色,包括强制性角色。对于存储程序,如果执行应该使用与默认不同的角色,则程序体可以执行SET ROLE来激活所需的角色。这必须谨慎进行,因为分配给角色的权限可以更改。

撤销角色或角色权限

就像角色可以授予给一个账户一样,它们也可以从一个账户中撤销:

REVOKE *role* FROM *user*;

mandatory_roles系统变量值中命名的角色不能被撤销。

REVOKE也可以应用于角色以修改授予其的权限。这不仅影响角色本身,还影响分配了该角色的任何帐户。假设您想暂时使所有应用程序用户只读。为此,请使用REVOKE来从app_write角色中撤销修改权限:

REVOKE INSERT, UPDATE, DELETE ON app_db.* FROM 'app_write';

正如所发生的那样,这将使角色完全没有任何权限,可以使用SHOW GRANTS来查看(这表明此语句可以与角色一起使用,而不仅仅是用户):

mysql> SHOW GRANTS FOR 'app_write';
+---------------------------------------+
| Grants for app_write@%                |
+---------------------------------------+
| GRANT USAGE ON *.* TO `app_write`@`%` |
+---------------------------------------+

因为从角色中撤销权限会影响分配了修改后角色的任何用户的权限,rw_user1现在没有表修改权限(INSERTUPDATEDELETE不再存在):

mysql> SHOW GRANTS FOR 'rw_user1'@'localhost'
       USING 'app_read', 'app_write';
+----------------------------------------------------------------+
| Grants for rw_user1@localhost                                  |
+----------------------------------------------------------------+
| GRANT USAGE ON *.* TO `rw_user1`@`localhost`                   |
| GRANT SELECT ON `app_db`.* TO `rw_user1`@`localhost`           |
| GRANT `app_read`@`%`,`app_write`@`%` TO `rw_user1`@`localhost` |
+----------------------------------------------------------------+

实际上,rw_user1读/写用户已成为只读用户。对于被授予app_write角色的任何其他帐户也是如此,说明使用角色使得不必为单个帐户修改权限。

要恢复角色的修改权限,只需重新授予它们:

GRANT INSERT, UPDATE, DELETE ON app_db.* TO 'app_write';

现在rw_user1再次具有修改权限,任何其他被授予app_write角色的帐户也是如此。

删除角色

要删除角色,请使用DROP ROLE

DROP ROLE 'app_read', 'app_write';

删除一个角色会从授予它的每个帐户中撤销该角色。

mandatory_roles系统变量值中命名的角色不能被删除。

用户和角色的互换性

正如早些时候暗示的那样,对于SHOW GRANTS,它显示用户帐户或角色的授权,帐户和角色可以互换使用。

角色和用户之间的一个区别是,CREATE ROLE默认创建一个被锁定的授权标识符,而CREATE USER默认创建一个未锁定的授权标识符。您应该记住这个区别不是不可改变的;具有适当权限的用户可以在创建后锁定或解锁角色或(其他)用户。

如果数据库管理员有一个偏好,即特定的授权标识符必须是一个角色,那么可以使用命名方案来传达这一意图。例如,您可以为所有您打算作为角色而不是其他内容的授权标识符使用r_前缀。

角色和用户之间的另一个区别在于用于管理它们的权限的可用性:

  • CREATE ROLEDROP ROLE 权限仅允许使用 CREATE ROLEDROP ROLE 语句。

  • CREATE USER 权限允许使用 ALTER USERCREATE ROLECREATE USERDROP ROLEDROP USERRENAME USERREVOKE ALL PRIVILEGES 语句。

因此,CREATE ROLEDROP ROLE 权限不如 CREATE USER 强大,可能授予那些只允许创建和删除角色而不执行更一般帐户操作的用户。

关于权限和用户与角色的可互换性,您可以将用户帐户视为角色并将该帐户授予另一个用户或角色。效果是将该帐户的权限和角色授予另一个用户或角色。

这组语句表明您可以将用户授予用户,将角色授予用户,将用户授予角色,或将角色授予角色:

CREATE USER 'u1';
CREATE ROLE 'r1';
GRANT SELECT ON db1.* TO 'u1';
GRANT SELECT ON db2.* TO 'r1';
CREATE USER 'u2';
CREATE ROLE 'r2';
GRANT 'u1', 'r1' TO 'u2';
GRANT 'u1', 'r1' TO 'r2';

每种情况的结果是将授予对象的权限与授予对象相关联的权限授予给受让对象。执行这些语句后,u2r2 分别从用户 (u1) 和角色 (r1) 获得了权限:

mysql> SHOW GRANTS FOR 'u2' USING 'u1', 'r1';
+-------------------------------------+
| Grants for u2@%                     |
+-------------------------------------+
| GRANT USAGE ON *.* TO `u2`@`%`      |
| GRANT SELECT ON `db1`.* TO `u2`@`%` |
| GRANT SELECT ON `db2`.* TO `u2`@`%` |
| GRANT `u1`@`%`,`r1`@`%` TO `u2`@`%` |
+-------------------------------------+
mysql> SHOW GRANTS FOR 'r2' USING 'u1', 'r1';
+-------------------------------------+
| Grants for r2@%                     |
+-------------------------------------+
| GRANT USAGE ON *.* TO `r2`@`%`      |
| GRANT SELECT ON `db1`.* TO `r2`@`%` |
| GRANT SELECT ON `db2`.* TO `r2`@`%` |
| GRANT `u1`@`%`,`r1`@`%` TO `r2`@`%` |
+-------------------------------------+

前面的例子仅供参考,但用户帐户和角色的可互换性具有实际应用,例如在以下情况下:假设一个传统的应用开发项目在 MySQL 的角色出现之前就开始了,因此与该项目相关的所有用户帐户都直接被授予权限(而不是通过授予角色而获得权限)。其中一个帐户是最初被授予权限的开发人员帐户如下:

CREATE USER 'old_app_dev'@'localhost' IDENTIFIED BY 'old_app_devpass';
GRANT ALL ON old_app.* TO 'old_app_dev'@'localhost';

如果该开发人员离开项目,则有必要将权限分配给另一个用户,或者如果开发活动已扩展,则可能需要分配给多个用户。以下是处理此问题的一些方法:

  • 不使用角色:更改帐户密码,以防止原始开发人员使用它,并让新开发人员使用该帐户:

    ALTER USER 'old_app_dev'@'localhost' IDENTIFIED BY '*new_password*';
    
  • 使用角色:锁定帐户以防止任何人使用它连接到服务器:

    ALTER USER 'old_app_dev'@'localhost' ACCOUNT LOCK;
    

    然后将该帐户视为一个角色。对于项目中的每个新开发人员,创建一个新帐户并授予其原始开发人员帐户:

    CREATE USER 'new_app_dev1'@'localhost' IDENTIFIED BY '*new_password*';
    GRANT 'old_app_dev'@'localhost' TO 'new_app_dev1'@'localhost';
    

    该效果是将原始开发者账户的权限分配给新账户。

8.2.11 账户类别

原文:dev.mysql.com/doc/refman/8.0/en/account-categories.html

从 MySQL 8.0.16 开始,MySQL 引入了基于 SYSTEM_USER 权限的用户账户类别概念。

  • 系统和常规账户

  • SYSTEM_USER 权限影响的操作

  • 系统和常规会话

  • 保护系统账户免受常规账户篡改

系统和常规账户

MySQL 引入了用户账户类别的概念,根据是否拥有 SYSTEM_USER 权限来区分系统用户和常规用户:

  • 拥有 SYSTEM_USER 权限的用户是系统用户。

  • 没有 SYSTEM_USER 权限的用户是常规用户。

SYSTEM_USER 权限对用户可以应用其它权限的账户产生影响,以及用户是否受到其他账户的保护:

  • 系统用户可以修改系统和常规账户。也就是说,拥有适当权限在常规账户上执行特定操作的用户,通过拥有 SYSTEM_USER 权限也能在系统账户上执行该操作。系统账户只能被拥有适当权限的系统用户修改,而不能被常规用户修改。

  • 拥有适当权限的常规用户可以修改常规账户,但不能修改系统账户。系统账户可以被拥有适当权限的系统用户和常规用户修改。

如果用户有适当权限在常规账户上执行特定操作,SYSTEM_USER 使用户也能在系统账户上执行该操作。SYSTEM_USER 不意味着任何其他权限,因此执行特定账户操作的能力仍取决于是否拥有任何其他所需权限。例如,如果用户可以授予常规账户 SELECTUPDATE 权限,那么拥有 SYSTEM_USER 的用户也可以授予系统账户 SELECTUPDATE 权限。

系统账户和普通账户之间的区别通过保护具有SYSTEM_USER特权的账户免受没有该特权的账户的影响,从而更好地控制某些账户管理问题。例如,CREATE USER特权不仅允许创建新账户,还允许修改和删除现有账户。没有系统用户概念,拥有CREATE USER特权的用户可以修改或删除任何现有账户,包括root账户。系统用户概念使得限制对root账户(本身是系统账户)的修改只能由系统用户进行。拥有CREATE USER特权的普通用户仍然可以修改或删除现有账户,但只能是普通账户。

SYSTEM_USER特权影响的操作

SYSTEM_USER特权影响以下操作:

  • 账户操作。

    账户操作包括创建和删除账户,授予和撤销特权,更改账户认证特性(如凭证或认证插件),以及更改其他账户特性,如密码过期策略。

    使用账户管理语句(如CREATE USERGRANT")操纵系统账户需要SYSTEM_USER特权。为防止账户以这种方式修改系统账户,将其设为普通账户,不授予SYSTEM_USER特权。(然而,要完全保护系统账户免受普通账户的修改,还必须对普通账户限制对mysql系统模式的修改特权。请参见保护系统账户免受普通账户操纵。)

  • 终止当前会话和其中执行的语句。

    要终止使用SYSTEM_USER特权执行的会话或语句,您自己的会话必须具有SYSTEM_USER特权,以及任何其他所需特权(CONNECTION_ADMIN或已弃用的SUPER特权)。

    从 MySQL 8.0.30 开始,如果将服务器置于脱机模式的用户没有SYSTEM_USER权限,则具有SYSTEM_USER权限的连接客户端用户也不会被断开连接。但是,这些用户在服务器处于脱机模式时无法启动新连接,除非他们还具有CONNECTION_ADMINSUPER权限。仅仅是他们现有的连接不会被终止,因为需要SYSTEM_USER权限才能执行该操作。

    在 MySQL 8.0.16 之前,CONNECTION_ADMIN权限(或已弃用的SUPER权限)足以终止任何会话或语句。

  • 为存储对象设置DEFINER属性。

    要将存储对象的DEFINER属性设置为具有SYSTEM_USER权限的帐户,您必须具有SYSTEM_USER权限,以及任何其他所需权限(SET_USER_ID或已弃用的SUPER权限)。

    在 MySQL 8.0.16 之前,SET_USER_ID权限(或已弃用的SUPER权限)足以为存储对象指定任何DEFINER值。

  • 指定强制角色。

    具有SYSTEM_USER权限的角色不能在mandatory_roles系统变量的值中列出。

    在 MySQL 8.0.16 之前,任何角色都可以列在mandatory_roles中。

  • 覆盖 MySQL Enterprise Audit 的审计日志过滤器中的“中止”项目。

    从 MySQL 8.0.28 开始,具有SYSTEM_USER权限的帐户会自动被分配AUDIT_ABORT_EXEMPT权限,因此即使审计日志过滤器中的“中止”项目会阻止它们,该帐户的查询也会始终执行。具有SYSTEM_USER权限的帐户因此可用于在审计配置错误后恢复对系统的访问。参见第 8.4.5 节,“MySQL Enterprise Audit”。

系统和常规会话

在服务器内执行的会��被区分为系统会话或常规会话,类似于系统和常规用户之间的区别:

  • 拥有SYSTEM_USER权限的会话是系统会话。

  • 不具有SYSTEM_USER权限的会话是普通会话。

普通会话只能执行普通用户允许的操作。系统会话还能执行仅允许系统用户的操作。

会话拥有的权限包括直接授予其基础帐户的权限,以及授予会话内所有当前活动角色的权限。因此,会话可能是系统会话,因为其帐户直接被授予SYSTEM_USER权限,或者因为会话已激活具有SYSTEM_USER权限的角色。授予帐户但在会话中未激活的角色不会影响会话权限。

因为激活和停用角色可以改变会话拥有的权限,会话可能从普通会话变为系统会话,反之亦然。如果会话激活或停用具有SYSTEM_USER权限的角色,则会话之间的普通和系统会话之间的适当变化立即发生,仅适用于该会话:

  • 如果普通会话激活具有SYSTEM_USER权限的角色,则该会话将变为系统会话。

  • 如果系统会话停用具有SYSTEM_USER权限的角色,则该会话将变为普通会话,除非仍有其他具有SYSTEM_USER权限的角色处于活动状态。

这些操作不会影响现有会话:

  • 如果向帐户授予或撤销SYSTEM_USER权限,则帐户的现有会话在普通会话和系统会话之间不会发生变化。授予或撤销操作仅影响帐户后续连接的会话。

  • 在会话中调用的存储对象执行的语句将以父会话的系统或普通状态执行,即使对象的DEFINER属性命名了系统帐户。

因为角色激活仅影响会话而不影响帐户,向普通帐户授予具有SYSTEM_USER权限的角色不会保护该帐户免受普通用户的攻击。该角色仅保护已激活该角色的帐户的会话,并仅保护会话免受普通会话的终止。

保护系统帐户免受普通帐户的操纵

帐户操作包括创建和删除帐户,授予和撤销权限,更改帐户身份验证特性,如凭据或身份验证插件,以及更改其他帐户特性,如密码过期策略。

帐户操作可以通过两种方式完成:

  • 通过使用诸如CREATE USERGRANT等帐户管理语句。这是首选方法。

  • 通过使用诸如INSERTUPDATE等语句直接修改授权表。虽然不鼓励这种方法,但对于具有mysql系统模式上适当特权的用户是可能的。

要完全保护系统帐户免受特定帐户的修改,将其设置为常规帐户,并不授予mysql模式的修改特权:

  • 使用帐户管理语句操作系统帐户需要SYSTEM_USER特权。为防止帐户以这种方式修改系统帐户,将其设置为常规帐户,不授予SYSTEM_USER。这包括不授予任何角色授予的帐户SYSTEM_USER

  • mysql模式的特权允许通过直接修改授权表来操作系统帐户,即使修改帐户是常规帐户。为了防止常规帐户通过直接修改未经授权地修改系统帐户,请不要向帐户(或任何授予帐户的角色)授予mysql模式的修改特权。如果常规帐户必须具有适用于所有模式的全局特权,则可以使用部分撤销施加的特权限制来阻止mysql模式的修改。参见 Section 8.2.12, “Privilege Restriction Using Partial Revokes”。

注意

与不授予SYSTEM_USER特权不同,阻止帐户修改系统帐户但不是常规帐户,不授予mysql模式特权会阻止帐户修改系统帐户以及常规帐户。这不应该是一个问题,因为如前所述,不鼓励直接修改授权表。

假设您想要创建一个名为u1的用户,该用户对所有模式具有所有特权,但u1应该是一个不能修改系统帐户的常规用户。假设partial_revokes系统变量已启用,配置u1如下:

CREATE USER u1 IDENTIFIED BY '*password*';

GRANT ALL ON *.* TO u1 WITH GRANT OPTION;
-- GRANT ALL includes SYSTEM_USER, so at this point
-- u1 can manipulate system or regular accounts

REVOKE SYSTEM_USER ON *.* FROM u1;
-- Revoking SYSTEM_USER makes u1 a regular user;
-- now u1 can use account-management statements
-- to manipulate only regular accounts

REVOKE ALL ON mysql.* FROM u1;
-- This partial revoke prevents u1 from directly
-- modifying grant tables to manipulate accounts

为防止帐户访问所有mysql系统模式,撤销其在mysql模式上的所有特权,如上所示。也可以允许部分mysql模式访问,例如只读访问。以下示例创建一个帐户,该帐户对所有模式具有全局的SELECTINSERTUPDATEDELETE特权,但对mysql模式仅具有SELECT特权:

CREATE USER u2 IDENTIFIED BY '*password*';
GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO u2;
REVOKE INSERT, UPDATE, DELETE ON mysql.* FROM u2;

另一种可能性是撤销所有mysql模式的权限,但授予对特定mysql表或列的访问权限。即使对mysql进行部分撤销,也可以完成此操作。以下语句允许在mysql模式内对u1进行只读访问,但仅限于db表以及user表的HostUser列:

CREATE USER u3 IDENTIFIED BY '*password*';
GRANT ALL ON *.* TO u3;
REVOKE ALL ON mysql.* FROM u3;
GRANT SELECT ON mysql.db TO u3;
GRANT SELECT(Host,User) ON mysql.user TO u3;

8.2.12 使用部分撤销进行权限限制

原文:dev.mysql.com/doc/refman/8.0/en/partial-revokes.html

在 MySQL 8.0.16 之前,不可能授予全局适用的权限,除了某些模式。从 MySQL 8.0.16 开始,如果启用了partial_revokes系统变量,则可以实现。具体来说,对于在全局级别具有权限的用户,partial_revokes使得可以撤销特定模式的权限,同时保留其他模式的权限。因此,施加的权限限制可能对具有全局权限但不应被允许访问某些模式的账户的管理很有用。例如,可以允许一个账户修改任何表,除了mysql系统模式中的表。

  • 使用部分撤销

  • 部分撤销与显式模式授权

  • 禁用部分撤销

  • 部分撤销和复制

注意

为简洁起见,此处显示的CREATE USER语句不包括密码。在生产环境中,请始终分配账户密码。

使用部分撤销

partial_revokes系统变量控制是否可以对账户施加权限限制。默认情况下,partial_revokes被禁用,尝试部分撤销全局权限会产生错误:

mysql> CREATE USER u1;
mysql> GRANT SELECT, INSERT ON *.* TO u1;
mysql> REVOKE INSERT ON world.* FROM u1;
ERROR 1141 (42000): There is no such grant defined for user 'u1' on host '%'

要允许REVOKE操作,请启用partial_revokes

SET PERSIST partial_revokes = ON;

SET PERSIST为运行中的 MySQL 实例设置一个值。它还保存该值,使其在后续服务器重启时保留。要更改运行中的 MySQL 实例的值,而不使其在后续重启时保留,使用GLOBAL关键字而不是PERSIST。参见 Section 15.7.6.1, “SET Syntax for Variable Assignment”。

启用partial_revokes后,部分撤销成功:

mysql> REVOKE INSERT ON world.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+------------------------------------------+
| Grants for u1@%                          |
+------------------------------------------+
| GRANT SELECT, INSERT ON *.* TO `u1`@`%`  |
| REVOKE INSERT ON `world`.* FROM `u1`@`%` |
+------------------------------------------+

SHOW GRANTS在其输出中将部分撤销列为REVOKE语句。结果表明u1具有全局SELECTINSERT权限,只是INSERT无法用于world模式中的表。也就是说,u1world表的访问是只读的。

服务器记录通过部分撤销在mysql.user系统表中实施的权限限制。如果一个账户有部分撤销,其User_attributes列的值具有Restrictions属性:

mysql> SELECT User, Host, User_attributes->>'$.Restrictions'
       FROM mysql.user WHERE User_attributes->>'$.Restrictions' <> '';
+------+------+------------------------------------------------------+
| User | Host | User_attributes->>'$.Restrictions'                   |
+------+------+------------------------------------------------------+
| u1   | %    | [{"Database": "world", "Privileges": ["INSERT"]}] |
+------+------+------------------------------------------------------+

注意

尽管可以对任何模式施加部分撤销,但对mysql系统模式的权限限制特别有用,作为防止常规账户修改系统账户的策略的一部分。请参阅防止常规账户操纵系统账户。

部分撤销操作受到以下条件的约束:

  • 可以使用部分撤销对不存在的模式施加限制,但只有在撤销的权限是全局授予的情况下才可以。如果权限没有全局授予,为不存在的模式撤销它会产生错误。

  • 部分撤销仅适用于模式级。您不能对仅全局适用的权限(如FILEBINLOG_ADMIN)或表、列或例程权限使用部分撤销。

  • 在权限分配中,启用partial_revokes会导致 MySQL 将模式名称中未转义的_% SQL 通配符字符解释为文字字符,就好像它们已经被转义为\_\%一样。因为这会改变 MySQL 解释权限的方式,建议在启用partial_revokes的安装中避免未转义的通配符字符在权限分配中出现。

如前所述,模式级权限的部分撤销在SHOW GRANTS输出中显示为REVOKE语句。这与SHOW GRANTS表示“普通”模式级权限的方式不同:

  • 当授予时,模式级权限通过其自己的GRANT语句在输出中表示:

    mysql> CREATE USER u1;
    mysql> GRANT UPDATE ON mysql.* TO u1;
    mysql> GRANT DELETE ON world.* TO u1;
    mysql> SHOW GRANTS FOR u1;
    +---------------------------------------+
    | Grants for u1@%                       |
    +---------------------------------------+
    | GRANT USAGE ON *.* TO `u1`@`%`        |
    | GRANT UPDATE ON `mysql`.* TO `u1`@`%` |
    | GRANT DELETE ON `world`.* TO `u1`@`%` |
    +---------------------------------------+
    
  • 当撤销时,模式级权限会简单地从输出中消失。它们不会显示为REVOKE语句:

    mysql> REVOKE UPDATE ON mysql.* FROM u1;
    mysql> REVOKE DELETE ON world.* FROM u1;
    mysql> SHOW GRANTS FOR u1;
    +--------------------------------+
    | Grants for u1@%                |
    +--------------------------------+
    | GRANT USAGE ON *.* TO `u1`@`%` |
    +--------------------------------+
    

当用户授予特权时,授予者对特权的任何限制都会被受让者继承,除非受让者已经拥有该特权而没有限制。考虑以下两个用户,其中一个拥有全局SELECT特权:

CREATE USER u1, u2;
GRANT SELECT ON *.* TO u2;

假设一个管理用户admin有一个全局但部分撤销的SELECT特权:

mysql> CREATE USER admin;
mysql> GRANT SELECT ON *.* TO admin WITH GRANT OPTION;
mysql> REVOKE SELECT ON mysql.* FROM admin;
mysql> SHOW GRANTS FOR admin;
+------------------------------------------------------+
| Grants for admin@%                                   |
+------------------------------------------------------+
| GRANT SELECT ON *.* TO `admin`@`%` WITH GRANT OPTION |
| REVOKE SELECT ON `mysql`.* FROM `admin`@`%`          |
+------------------------------------------------------+

如果admin全局授予u1u2``SELECT,则每个用户的结果不同:

  • 如果admin全局授予u1``SELECT,而u1一开始没有SELECT特权,u1会继承admin的特权限制:

    mysql> GRANT SELECT ON *.* TO u1;
    mysql> SHOW GRANTS FOR u1;
    +------------------------------------------+
    | Grants for u1@%                          |
    +------------------------------------------+
    | GRANT SELECT ON *.* TO `u1`@`%`          |
    | REVOKE SELECT ON `mysql`.* FROM `u1`@`%` |
    +------------------------------------------+
    
  • 另一方面,u2已经拥有全局SELECT特权而没有限制。GRANT只能添加到受让者的现有特权,而不能减少它们,因此如果admin全局授予u2``SELECTu2不会继承admin的限制:

    mysql> GRANT SELECT ON *.* TO u2;
    mysql> SHOW GRANTS FOR u2;
    +---------------------------------+
    | Grants for u2@%                 |
    +---------------------------------+
    | GRANT SELECT ON *.* TO `u2`@`%` |
    +---------------------------------+
    

如果GRANT语句包括一个AS *user*子句,应用的特权限制是子句指定的用户/角色组合上的,而不是执行该语句的用户上的。有关AS子句的信息,请参见 Section 15.7.1.6, “GRANT Statement”。

授予给账户的新特权的限制将添加到该账户的任何现有限制中:

mysql> CREATE USER u1;
mysql> GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO u1;
mysql> REVOKE INSERT ON mysql.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+---------------------------------------------------------+
| Grants for u1@%                                         |
+---------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `u1`@`%` |
| REVOKE INSERT ON `mysql`.* FROM `u1`@`%`                |
+---------------------------------------------------------+
mysql> REVOKE DELETE, UPDATE ON db2.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+---------------------------------------------------------+
| Grants for u1@%                                         |
+---------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `u1`@`%` |
| REVOKE UPDATE, DELETE ON `db2`.* FROM `u1`@`%`          |
| REVOKE INSERT ON `mysql`.* FROM `u1`@`%`                |
+---------------------------------------------------------+

特权限制的聚合既适用于显式部分撤销特权(如刚刚显示的)又适用于从执行语句的用户或在AS *user*子句中提到的用户隐式继承限制时。

如果账户在模式上有特权限制:

  • 该账户无法向其他账户授予对受限模式或其中任何对象的特权。

  • 另一个没有限制的账户可以向受限账户授予受限模式或其中对象的特权。假设一个无限制用户执行以下语句:

    CREATE USER u1;
    GRANT SELECT, INSERT, UPDATE ON *.* TO u1;
    REVOKE SELECT, INSERT, UPDATE ON mysql.* FROM u1;
    GRANT SELECT ON mysql.user TO u1;          -- grant table privilege
    GRANT SELECT(Host,User) ON mysql.db TO u1; -- grant column privileges
    

    结果账户具有这些特权,能够在受限模式内执行有限操作:

    mysql> SHOW GRANTS FOR u1;
    +-----------------------------------------------------------+
    | Grants for u1@%                                           |
    +-----------------------------------------------------------+
    | GRANT SELECT, INSERT, UPDATE ON *.* TO `u1`@`%`           |
    | REVOKE SELECT, INSERT, UPDATE ON `mysql`.* FROM `u1`@`%`  |
    | GRANT SELECT (`Host`, `User`) ON `mysql`.`db` TO `u1`@`%` |
    | GRANT SELECT ON `mysql`.`user` TO `u1`@`%`                |
    +-----------------------------------------------------------+
    

如果账户对全局特权有限制,这些操作中的任何一个都会移除限制:

  • 由没有对特权限制的账户全局授予账户特权。

  • 在模式级别授予特权。

  • 全局撤销特权。

考虑一个全局拥有多个特权但对INSERTUPDATEDELETE有限制的用户u1

mysql> CREATE USER u1;
mysql> GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO u1;
mysql> REVOKE INSERT, UPDATE, DELETE ON mysql.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+----------------------------------------------------------+
| Grants for u1@%                                          |
+----------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `u1`@`%`  |
| REVOKE INSERT, UPDATE, DELETE ON `mysql`.* FROM `u1`@`%` |
+----------------------------------------------------------+

从没有限制的帐户全局授予u1权限会移除权限限制。例如,要移除INSERT限制:

mysql> GRANT INSERT ON *.* TO u1;
mysql> SHOW GRANTS FOR u1;
+---------------------------------------------------------+
| Grants for u1@%                                         |
+---------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `u1`@`%` |
| REVOKE UPDATE, DELETE ON `mysql`.* FROM `u1`@`%`        |
+---------------------------------------------------------+

在模式级别向u1授予权限会移除权限限制。例如,要移除UPDATE限制:

mysql> GRANT UPDATE ON mysql.* TO u1;
mysql> SHOW GRANTS FOR u1;
+---------------------------------------------------------+
| Grants for u1@%                                         |
+---------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `u1`@`%` |
| REVOKE DELETE ON `mysql`.* FROM `u1`@`%`                |
+---------------------------------------------------------+

撤销全局特权会移除该特权,包括任何对其的限制。例如,要移除DELETE限制(以牺牲所有DELETE访问权限为代价):

mysql> REVOKE DELETE ON *.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+-------------------------------------------------+
| Grants for u1@%                                 |
+-------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE ON *.* TO `u1`@`%` |
+-------------------------------------------------+

如果一个帐户在全局和模式级别都有权限,则必须在模式级别撤销两次才能实现部分撤销。假设u1拥有这些权限,其中INSERT在全局和world模式上都有:

mysql> CREATE USER u1;
mysql> GRANT SELECT, INSERT ON *.* TO u1;
mysql> GRANT INSERT ON world.* TO u1;
mysql> SHOW GRANTS FOR u1;
+-----------------------------------------+
| Grants for u1@%                         |
+-----------------------------------------+
| GRANT SELECT, INSERT ON *.* TO `u1`@`%` |
| GRANT INSERT ON `world`.* TO `u1`@`%`   |
+-----------------------------------------+

撤销world上的INSERT会撤销模式级别的特权(SHOW GRANTS不再显示模式级别的GRANT语句):

mysql> REVOKE INSERT ON world.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+-----------------------------------------+
| Grants for u1@%                         |
+-----------------------------------------+
| GRANT SELECT, INSERT ON *.* TO `u1`@`%` |
+-----------------------------------------+

再次撤销world上的INSERT执行全局特权的部分撤销(SHOW GRANTS现在包括模式级别的REVOKE语句):

mysql> REVOKE INSERT ON world.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+------------------------------------------+
| Grants for u1@%                          |
+------------------------------------------+
| GRANT SELECT, INSERT ON *.* TO `u1`@`%`  |
| REVOKE INSERT ON `world`.* FROM `u1`@`%` |
+------------------------------------------+

部分撤销与明确的模式授予

为某些模式提供帐户访问权限,但不提供其他模式的访问权限,部分撤销提供了一种替代方法,可以在不授予全局特权的情况下明确授予模式级别的访问权限。这两种方法各有优缺点。

授予模式级别特权而不是全局特权:

  • 添加新模式:默认情况下,现有帐户无法访问该模式。对于任何应该访问该模式的帐户,DBA 必须授予模式级别的访问权限。

  • 添加新帐户:DBA 必须为每个应该访问的模式授予模式级别的访问权限。

在与部分撤销一起授予全局特权:

  • 添加新模式:对于具有全局特权的现有帐户,可以访问该模式。对于任何应该无法访问该模式的帐户,DBA 必须添加部分撤销。

  • 添加新帐户:DBA 必须授予全局特权,以及对每个受限制的模式进行部分撤销。

使用明确的模式级别授予方法更方便,适用于访问仅限于少数模式的帐户。使用部分撤销的方法更适用于对所有模式具有广泛访问权限的帐户,除了少数模式。

禁用部分撤销

一旦启用,partial_revokes 如果任何帐户具有特权限制,则无法禁用。如果存在这样的帐户,则禁用 partial_revokes 将失败:

  • 尝试在启动时禁用 partial_revokes 时,服务器会记录错误消息并启用 partial_revokes

  • 尝试在运行时禁用 partial_revokes 时,会发生错误,并且 partial_revokes 的值保持不变。

要在存在限制时禁用 partial_revokes,必须首先移除限制:

  1. 确定哪些帐户具有部分撤销:

    SELECT User, Host, User_attributes->>'$.Restrictions'
    FROM mysql.user WHERE User_attributes->>'$.Restrictions' <> '';
    
  2. 对于每个这样的帐户,删除其特权限制。假设前一步显示帐户 u1 具有这些限制:

    [{"Database": "world", "Privileges": ["INSERT", "DELETE"]
    

    可以通过多种方式进行限制移除:

    • 全局授予权限,无限制:

      GRANT INSERT, DELETE ON *.* TO u1;
      
    • 在模式级别授予权限:

      GRANT INSERT, DELETE ON world.* TO u1;
      
    • 全局撤销特权(假设不再需要):

      REVOKE INSERT, DELETE ON *.* FROM u1;
      
    • 删除帐户本身(假设不再需要):

      DROP USER u1;
      

删除所有特权限制后,可以禁用部分撤销:

SET PERSIST partial_revokes = OFF;

部分撤销和复制

在复制场景中,如果任何主机上启用了 partial_revokes,则所有主机上都必须启用。否则,对于在复制发生的所有主机上部分撤销全局特权的 REVOKE 语句可能不会对所有主机产生相同的效果,可能导致复制不一致或错误。

当启用 partial_revokes 时,GRANT 语句的二进制日志中记录了扩展语法,包括发出该语句的当前用户及其当前活动角色。如果以这种方式记录的用户或角色在副本上不存在,则复制应用程序线程会在带有错误的 GRANT 语句处停止。确保在复制源服务器上发出或可能发出 GRANT 语句的所有用户帐户也存在于副本上,并且具有与源服务器上相同的角色集。

8.2.13 权限更改何时生效

原文:dev.mysql.com/doc/refman/8.0/en/privilege-changes.html

如果mysqld服务器在没有--skip-grant-tables选项的情况下启动,它在启动序列期间将所有授权表内容读入内存。在那一点上,内存中的表对访问控制生效。

如果您间接修改授权表,使用帐户管理语句,服务器会立即注意到这些更改并重新加载授权表到内存中。帐户管理语句在第 15.7.1 节“帐户管理语句”中描述。例如包括GRANTREVOKESET PASSWORDRENAME USER

如果您直接修改授权表,使用诸如INSERTUPDATEDELETE等语句(不建议这样做),则在告知服务器重新加载表或重新启动之前,更改不会影响权限检查。因此,如果您直接更改授权表但忘记重新加载它们,更改将不会生效,直到重新启动服务器。这可能会让您想知道为什么您的更改似乎没有任何影响!

要告知服务器重新加载授权表,请执行刷新权限操作。这可以通过发出FLUSH PRIVILEGES语句或执行mysqladmin flush-privilegesmysqladmin reload命令来完成。

重新加载授权表会影响每个现有客户端会话的权限:

  • 表和列权限更改将在客户端的下一个请求中生效。

  • 数据库权限更改将在客户端执行USE *db_name*语句的下一次生效。

    注意

    客户端应用程序可能会缓存数据库名称;因此,这种效果可能对它们不可见,除非实际更改为不同的数据库。

  • 静态全局权限和密码对已连接的客户端不受影响。这些更改仅在后续连接的会话中生效。动态全局权限的更改立即生效。有关静态和动态权限之间的区别,请参阅静态与动态权限。

会话中活动角色集的更改立即生效,仅对该会话有效。SET ROLE语句执行会话角色的激活和停用(参见第 15.7.1.11 节,“SET ROLE Statement”)。

如果服务器使用--skip-grant-tables选项启动,则不会读取授权表或实施任何访问控制。任何用户都可以连接并执行任何操作,这是不安全的。要使这样启动的服务器读取表并启用访问检查,请刷新权限。

8.2.14 分配帐户密码

原文:dev.mysql.com/doc/refman/8.0/en/assigning-passwords.html

连接到 MySQL 服务器的客户端所需的凭据可能包括密码。本节描述了如何为 MySQL 帐户分配密码。

MySQL 将凭据存储在mysql系统数据库中的user表中。仅允许具有CREATE USER权限的用户执行分配或修改密码的操作,或者具有mysql数据库的权限(INSERT权限用于创建新帐户,UPDATE权限用于修改现有帐户)。如果启用了read_only系统变量,则使用诸如CREATE USERALTER 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 USERALTER 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_nameHost列中的从中连接的客户端主机匹配的行的帐户。

警告

使用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 密码管理

原文:dev.mysql.com/doc/refman/8.0/en/password-management.html

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 USERALTER 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_historypassword_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 USERALTER USER语句的PASSWORD HISTORYPASSWORD 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 HISTORYPASSWORD 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 USERALTER USER语句的PASSWORD REQUIRE选项建立:

  • 如果账户设置为PASSWORD REQUIRE CURRENT,密码更改必须指定当前密码。

  • 如果账户设置为PASSWORD REQUIRE CURRENT OPTIONAL,密码更改可能需要但不一定需要指定当前密码。

  • 如果账户设置为PASSWORD REQUIRE CURRENT DEFAULTpassword_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 USERALTER 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 USERSET 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被省略。

双密码支持

截至 MySQL 8.0.14,用户帐户被允许具有主密码和辅助密码,称为双密码。双密码功能使得在诸如此类场景中无缝执行凭据更改成为可能:

  • 一个系统有大量的 MySQL 服务器,可能涉及复制。

  • 多个应用程序连接到不同的 MySQL 服务器。

  • 必须定期更改用于连接到服务器的应用程序使用的帐户或帐户的凭据。

考虑在前述类型的场景中执行凭据更改时,当一个账户只允许一个密码时。在这种情况下,必须在账户密码更改和在所有服务器上传播时以及所有使用该账户的应用程序更新以使用新密码的时间上进行紧密合作。这个过程可能涉及停机,期间服务器或应用程序不可用。

通过双重密码,可以更轻松地分阶段进行凭据更改,无需紧密合作,也无需停机:

  1. 对于每个受影响的账户,在服务器上建立一个新的主密码,将当前密码保留为次要密码。这使得服务器可以识别每个账户的主密码或次要密码,而应用程序可以继续使用与以前相同的密码连接到服务器(现在是次要密码)。

  2. 在密码更改传播到所有服务器后,修改使用任何受影响账户的应用程序,以使用账户主密码连接。

  3. 在所有应用程序已从次要密码迁移到主密码后,次要密码不再需要,可以丢弃。此更改传播到所有服务器后,每个账户只能使用主密码连接。凭据更改现在已完成。

MySQL 使用保存和丢弃次要密码的语法实现双重密码功能:

  • RETAIN CURRENT PASSWORD子句用于ALTER USERSET PASSWORD语句,当您分配新的主密码时,会将账户当前密码保存为其次要密码。

  • DISCARD OLD PASSWORD子句用于ALTER USER,丢弃账户次要密码,仅保留主密码。

假设对于先前描述的凭据更改场景,一个名为'appuser1'@'host1.example.com'的账户被应用程序用于连接服务器,并且要将账户密码从'*password_a*'更改为'*password_b*'

要执行凭据更改,请使用以下ALTER USER

  1. 在每个非副本服务器上,建立'*password_b*'作为新的appuser1主密码,保留当前密码作为次要密码:

    ALTER USER 'appuser1'@'host1.example.com'
      IDENTIFIED BY '*password_b*'
      RETAIN CURRENT PASSWORD;
    
  2. 等待密码更改在整个系统中复制到所有副本。

  3. 修改使用appuser1账户的每个应用程序,使其使用密码'*password_b*'而不是'*password_a*'连接到服务器。

  4. 此时,次要密码不再需要。在每个非副本服务器上丢弃次要密码:

    ALTER USER 'appuser1'@'host1.example.com'
      DISCARD OLD PASSWORD;
    
  5. 在丢弃密码更改已经复制到所有副本之后,凭据更改完成。

RETAIN CURRENT PASSWORDDISCARD OLD PASSWORD 子句具有以下效果:

  • RETAIN CURRENT PASSWORD 会保留账户当前密码作为其二级密码,替换任何现有的二级密码。新密码成为主密码,但客户端可以使用主密码或二级密码连接到服务器。 (例外情况:如果由 ALTER USERSET PASSWORD 语句指定的新密码为空,则即使给出 RETAIN CURRENT PASSWORD,二级密码也会变为空。)

  • 如果您为具有空主密码的账户指定 RETAIN CURRENT PASSWORD,则该语句将失败。

  • 如果一个账户有二级密码,并且您更改其主密码而不指定 RETAIN CURRENT PASSWORD,则二级密码保持不变。

  • 对于 ALTER USER,如果更改分配给账户的身份验证插件,则二级密码将被丢弃。 如果更改身份验证插件并且还指定 RETAIN CURRENT PASSWORD,则该语句将失败。

  • 对于 ALTER USERDISCARD OLD PASSWORD 会丢弃二级密码(如果存在)。账户仅保留其主密码,客户端只能使用主密码连接到服务器。

修改二级密码的语句需要以下权限:

  • 要使用 RETAIN CURRENT PASSWORDDISCARD OLD PASSWORD 子句来操作您自己的账户的 ALTER USERSET PASSWORD 语句,需要 APPLICATION_PASSWORD_ADMIN 权限。 大多数用户只需要一个密码,因此需要该权限来操作自己的二级密码。

  • 如果要允许一个账户为所有账户操作二级密码,则应授予 CREATE USER 权限,而不是 APPLICATION_PASSWORD_ADMIN

随机密码生成

截至 MySQL 8.0.18 版本,CREATE USERALTER USERSET PASSWORD 语句具有生成用户账户随机密码的功能,作为明文密码的替代选择。有关语法的详细信息,请参阅每个语句的描述。本节描述了生成随机密码的共同特征。

默认情况下,生成的随机密码长度为 20 个字符。这个长度由generated_random_password_length系统变量控制,范围从 5 到 255。

对于每个语句生成随机密码的账户,该语句将密码存储在mysql.user系统表中,适当地为账户认证插件进行哈希处理。该语句还会在结果集的一行中返回明文密码,以便用户或应用程序执行该语句。结果集列名为userhostgenerated passwordauth_factor,表示标识mysql.user系统表中受影响行的用户名和主机名值,明文生成的密码,以及显示密码值适用的认证因素。

mysql> CREATE USER
       'u1'@'localhost' IDENTIFIED BY RANDOM PASSWORD,
       'u2'@'%.example.com' IDENTIFIED BY RANDOM PASSWORD,
       'u3'@'%.org' IDENTIFIED BY RANDOM PASSWORD;
+------+---------------+----------------------+-------------+
| user | host          | generated password   | auth_factor |
+------+---------------+----------------------+-------------+
| u1   | localhost     | iOeqf>Mh9:;XD&qn(Hl} |           1 |
| u2   | %.example.com | sXTSAEvw3St-R+_-C3Vb |           1 |
| u3   | %.org         | nEVe%Ctw/U/*Md)Exc7& |           1 |
+------+---------------+----------------------+-------------+
mysql> ALTER USER
       'u1'@'localhost' IDENTIFIED BY RANDOM PASSWORD,
       'u2'@'%.example.com' IDENTIFIED BY RANDOM PASSWORD;
+------+---------------+----------------------+-------------+
| user | host          | generated password   | auth_factor |
+------+---------------+----------------------+-------------+
| u1   | localhost     | Seiei:&cw}8]@3OA64vh |           1 |
| u2   | %.example.com | j@&diTX80l8}(NiHXSae |           1 |
+------+---------------+----------------------+-------------+
mysql> SET PASSWORD FOR 'u3'@'%.org' TO RANDOM;
+------+-------+----------------------+-------------+
| user | host  | generated password   | auth_factor |
+------+-------+----------------------+-------------+
| u3   | %.org | n&cz2xF;P3!U)+]Vw52H |           1 |
+------+-------+----------------------+-------------+

一个CREATE USERALTER USERSET PASSWORD语句,为一个账户生成一个随机密码,被写入二进制日志作为一个带有IDENTIFIED WITH *auth_plugin* AS '*auth_string*'子句的CREATE USERALTER USER语句,其中*auth_plugin*是账户认证插件,'auth_string'是账户哈希密码值。

如果安装了validate_password组件,则其实施的策略对生成的密码没有影响。(密码验证的目的是帮助人类创建更好的密码。)

登录失败跟踪和临时账户锁定

从 MySQL 8.0.19 开始,管理员可以配置用户账户,使得连续登录失败次数过多会导致临时账户锁定。

在这种情况下,“登录失败”意味着客户端在连接尝试期间未提供正确密码。这不包括由于未知用户或网络问题等原因而无法连接的情况。对于具有双重密码的账户(参见双重密码支持),任一账户密码都算正确。

每个账户的所需登录失败次数和锁定时间可通过CREATE USERALTER USER语句的FAILED_LOGIN_ATTEMPTSPASSWORD_LOCK_TIME选项进行配置。示例:

CREATE USER 'u1'@'localhost' IDENTIFIED BY '*password*'
  FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME 3;

ALTER USER 'u2'@'localhost'
  FAILED_LOGIN_ATTEMPTS 4 PASSWORD_LOCK_TIME UNBOUNDED;

当连续登录失败次数过多时,客户端会收到如下错误消息:

ERROR 3957 (HY000): Access denied for user *user*.
Account is blocked for *D* day(s) (*R* day(s) remaining)
due to *N* consecutive failed logins.

使用选项如下:

  • FAILED_LOGIN_ATTEMPTS *N*

    此选项指示是否跟踪指定了不正确密码的账户登录尝试。数字N指定了多少个连续的不正确密码会导致临时账户锁定。

  • PASSWORD_LOCK_TIME {*N* | UNBOUNDED}

    此选项指示在太多连续登录尝试提供错误密码后锁定帐户的时间。该值是一个数字N,用于指定帐户保持锁定状态的天数,或UNBOUNDED以指定当帐户进入临时锁定状态时,该状态的持续时间是无限的,并且直到帐户被解锁为止。解锁发生的条件将在后面描述。

每个选项的允许值N的范围为 0 到 32767。值为 0 会禁用该选项。

失败登录跟踪和临时帐户锁定具有以下特征:

  • 要对帐户进行失败登录跟踪和临时锁定,其FAILED_LOGIN_ATTEMPTSPASSWORD_LOCK_TIME选项都必须为非零。

  • 对于CREATE USER,如果未指定FAILED_LOGIN_ATTEMPTSPASSWORD_LOCK_TIME,则其隐式默认值对于语句命名的所有帐户均为 0。这意味着禁用了失败登录跟踪和临时帐户锁定。(这些隐式默认值也适用于在引入失败登录跟踪之前创建的帐户。)

  • 对于ALTER USER,如果未指定FAILED_LOGIN_ATTEMPTSPASSWORD_LOCK_TIME,则其值对于语句命名的所有帐户保持不变。

  • 要发生临时帐户锁定,密码失败必须是连续的。在达到失败登录的FAILED_LOGIN_ATTEMPTS值之前发生的任何成功登录都会导致失败计数重置。例如,如果FAILED_LOGIN_ATTEMPTS为 4,并且已发生三次连续密码失败,则需要再次失败一次才能开始锁定。但是,如果下次登录成功,则帐户的失败登录计数将被重置,因此再次需要四次连续失败才能锁定。

  • 一旦临时锁定开始,即使使用正确密码也无法进行成功登录,直到锁定持续时间已过或帐户通过以下讨论中列出的帐户重置方法之一被解锁。

当服务器读取授权表时,它会初始化每个帐户的状态信息,包括是否启用了失败登录跟踪,帐户当前是否被临时锁定以及如果是的话锁定何时开始,以及如果帐户未被锁定,则在临时锁定发生之前的失败次数。

帐户的状态信息可以被重置,这意味着失败登录计数被重置,并且如果当前被临时锁定,则帐户被解锁。帐户重置可以是全局的,适用于所有帐户,也可以是每个帐户:

  • 所有帐户的全局重置发生在以下任何条件下:

    • 服务器重新启动。

    • 执行FLUSH PRIVILEGES。(使用--skip-grant-tables选项启动服务器会导致授予权限表不被读取,从而禁用了失败登录跟踪。在这种情况下,第一次执行FLUSH PRIVILEGES会导致服务器读取授予权限表并启用失败登录跟踪,同时重置所有账户。)

  • 对于任何以下条件,每个账户都会发生重置:

    • 账户成功登录。

    • 锁定持续时间过去。在这种情况下,失败登录计数将在下次登录尝试时重置。

    • 对于账户执行设置FAILED_LOGIN_ATTEMPTSPASSWORD_LOCK_TIME(或两者)任何值(包括当前选项值)的ALTER USER语句的执行,或对于账户执行ALTER USER ... UNLOCK语句。

      对于账户的其他ALTER USER语句对其当前的失败登录计数或锁定状态没有影响。

失败登录跟踪与用于检查凭据的登录账户相关联。如果使用用户代理,跟踪将针对代理用户而不是被代理用户进行。也就是说,跟踪与USER()指示的账户相关联,而不是与CURRENT_USER()指示的账户相关联。有关代理用户和被代理用户之间区别的信息,请参阅第 8.2.19 节,“代理用户”。

8.2.16 服务器处理过期密码

原文:dev.mysql.com/doc/refman/8.0/en/expired-password-handling.html

MySQL 提供了密码过期功能,使数据库管理员可以要求用户重置他们的密码。密码可以手动过期,也可以根据自动过期策略(参见第 8.2.15 节,“密码管理”)。

ALTER USER语句可以启用账户密码过期。例如:

ALTER USER 'myuser'@'localhost' PASSWORD EXPIRE;

对于使用过期密码的账户的每个连接,服务器要么断开客户端连接,要么将客户端限制在“沙盒模式”中,其中服务器只允许客户端执行重置过期密码所需的操作。服务器采取的操作取决于客户端和服务器设置,稍后将讨论。

如果服务器断开客户端连接,它会返回一个ER_MUST_CHANGE_PASSWORD_LOGIN错误:

$> mysql -u myuser -p
Password: ******
ERROR 1862 (HY000): Your password has expired. To log in you must
change it using a client that supports expired passwords.

如果服务器将客户端限制在沙盒模式中,客户端会话内允许执行以下操作:

  • 客户端可以使用ALTER USERSET PASSWORD重置账户密码。完成后,服务器将为该会话以及使用该账户的后续连接恢复正常访问。

    注意

    尽管可以通过将过期密码重置为当前值来“重置”过期密码,但作为良好策略,最好选择一个不同的密码。数据库管理员可以通过建立适当的密码重用策略来强制执行不重复使用。参见密码重用策略。

  • 在 MySQL 8.0.27 之前,客户端可以使用SET语句。从 MySQL 8.0.27 开始,不再允许此操作。

对于会话中不允许的任何操作,服务器会返回一个ER_MUST_CHANGE_PASSWORD错误:

mysql> USE performance_schema;
ERROR 1820 (HY000): You must reset your password using ALTER USER
statement before executing this statement. 
mysql> SELECT 1;
ERROR 1820 (HY000): You must reset your password using ALTER USER
statement before executing this statement.

这通常发生在交互式调用mysql客户端时,因为默认情况下这种调用会被放置在沙盒模式中。要恢复正常功能,请选择一个新密码。

对于mysql客户端的非交互式调用(例如,在批处理模式下),如果密码过期,服务器通常会断开客户端的连接。为了允许非交互式的mysql调用保持连接,以便可以更改密码(使用沙盒模式中允许的语句),请在mysql命令中添加--connect-expired-password选项。

如前所述,服务器是否断开过期密码客户端的连接或将其限制在沙盒模式取决于客户端和服务器设置的组合。以下讨论描述了相关设置及其交互方式。

注意

此讨论仅适用于密码过期的帐户。如果客户端使用未过期的密码连接,则服务器会正常处理客户端。

在客户端端,给定的客户端指示它是否可以处理过期密码的沙盒模式。对于使用 C 客户端库的客户端,有两种方法可以做到这一点:

  • 在连接之前向mysql_options()传递MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS标志:

    bool arg = 1;
    mysql_options(mysql,
                  MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,
                  &arg);
    

    这是在mysql客户端中使用的技术,如果以交互方式调用或使用--connect-expired-password选项,则启用MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS

  • 在连接时向mysql_real_connect()传递CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS标志:

    MYSQL mysql;
    mysql_init(&mysql);
    if (!mysql_real_connect(&mysql,
                            host, user, password, db,
                            port, unix_socket,
                            CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS))
    {
      ... handle error ...
    }
    

其他 MySQL 连接器有其自己的约定,用于指示准备处理沙盒模式。请参阅您感兴趣的连接器的文档。

在服务器端,如果客户端指示它可以处理过期密码,服务器会将其置于沙盒模式。

如果客户端没有指示它可以处理过期密码(或者使用无法指示的较旧版本的客户端库),则服务器的操作取决于disconnect_on_expired_password系统变量的值:

  • 如果disconnect_on_expired_password已启用(默认情况下),服务器将以ER_MUST_CHANGE_PASSWORD_LOGIN错误断开客户端的连接。

  • 如果disconnect_on_expired_password已禁用,则服务器将客户端置于沙盒模式。

8.2.17 可插拔认证

原文:dev.mysql.com/doc/refman/8.0/en/pluggable-authentication.html

当客户端连接到 MySQL 服务器时,服务器使用客户端提供的用户名和客户端主机来从mysql.user系统表中选择适当的账户行。然后服务器对客户端进行认证,从账户行确定适用于客户端的认证插件:

  • 如果服务器找不到插件,则会发生错误并拒绝连接尝试。

  • 否则,服务器会调用该插件来对用户进行认证,插件会向服务器返回一个状态,指示用户是否提供了正确的密码并被允许连接。

可插拔认证使得以下重要功能成为可能:

  • 认证方法的选择。 可插拔的认证使得数据库管理员可以轻松选择和更改用于各个 MySQL 账户的认证方法。

  • 外部认证。 可插拔认证使得客户端可以使用适合于在mysql.user系统表之外存储凭据的认证方法进行连接到 MySQL 服务器。例如,可以创建插件来使用外部认证方法,如 PAM、Windows 登录 ID、LDAP 或 Kerberos。

  • 代理用户: 如果用户被允许连接,认证插件可以向服务器返回一个与连接用户名称不同的用户名,以指示连接用户是另一个用户(被代理用户)的代理。在连接持续期间,代理用户在访问控制方面被视为具有被代理用户的权限。实际上,一个用户冒充另一个用户。更多信息,请参见第 8.2.19 节,“代理用户”。

注意

如果使用--skip-grant-tables选项启动服务器,则即使加载了认证插件,也不会使用认证插件,因为服务器不执行客户端认证并允许任何客户端连接。由于这是不安全的,如果使用--skip-grant-tables选项启动服务器,则还会通过启用skip_networking来禁用远程连接。

  • 可用认证插件

  • 默认认证插件

  • 认证插件使用

  • 认证插件客户端/服务器兼容性

  • 认证插件连接器编写注意事项

  • 可插拔认证的限制

可用的认证插件

MySQL 8.0 提供了这些认证插件:

  • 一个执行本地认证的插件;即,在 MySQL 引入可插拔认证之前使用的基于密码哈希方法的认证。mysql_native_password插件实现了基于此本地密码哈希方法的认证。参见 Section 8.4.1.1,“本地可插拔认证”。

    注意

    截至 MySQL 8.0.34,mysql_native_password认证插件已被弃用,并可能在将来的 MySQL 版本中被移除。

  • 使用 SHA-256 密码哈希进行认证的插件。这比本地认证提供的加密更强大。参见 Section 8.4.1.2,“缓存 SHA-2 可插拔认证”,以及 Section 8.4.1.3,“SHA-256 可插拔认证”。

  • 一个客户端插件,将密码以明文形式发送到服务器,不经过哈希或加密。此插件与需要以客户端用户提供的密码完全一致的服务器端插件一起使用。参见 Section 8.4.1.4,“客户端明文插件认证”。

  • 一个使用 PAM(可插拔认证模块)执行外部认证的插件,使 MySQL 服务器能够使用 PAM 对 MySQL 用户进行认证。此插件还支持代理用户。参见 Section 8.4.1.5,“PAM 可插拔认证”。

  • 一个在 Windows 上执行外部认证的插件,使 MySQL 服务器能够使用本地 Windows 服务对客户端连接进行认证。已登录到 Windows 的用户可以根据其环境中的信息从 MySQL 客户端程序连接到服务器,而无需指定额外的密码。此插件还支持代理用户。参见 Section 8.4.1.6,“Windows 可插拔认证”。

  • 使用 LDAP(轻量级目录访问协议)进行认证的插件,通过访问诸如 X.500 之类的目录服务来认证 MySQL 用户。这些插件还支持代理用户。参见第 8.4.1.7 节,“LDAP 可插拔认证”。

  • 一个使用 Kerberos 进行认证的插件,用于认证与 Kerberos 主体对应的 MySQL 用户。参见第 8.4.1.8 节,“Kerberos 可插拔认证”。

  • 一个阻止所有使用它的帐户的客户端连接的插件。此插件的用例包括永远不允许直接登录但仅通过代理帐户访问的代理帐户和必须能够以提升的权限执行存储程序和视图而不向普通用户公开这些权限的帐户。参见第 8.4.1.9 节,“无登录可插拔认证”。

  • 一个插件,用于验证通过 Unix 套接字文件从本地主机连接的客户端。参见第 8.4.1.10 节,“套接字对等凭证可插拔认证”。

  • 一个通过 FIDO 认证将用户认证到 MySQL 服务器的插件。参见第 8.4.1.11 节,“FIDO 可插拔认证”。

    注意

    截至 MySQL 8.0.35 版本,authentication_fidoauthentication_fido_client认证插件已被弃用,并可能在将来的 MySQL 版本中被移除。

  • 一个测试插件,检查帐户凭据并将成功或失败记录到服务器错误日志中。此插件旨在用于测试和开发目的,并作为编写认证插件的示例。参见第 8.4.1.12 节,“测试可插拔认证”。

注意

有关当前对可插拔认证使用的限制的信息,包括哪些连接器支持哪些插件,请参阅可插拔认证限制。

第三方连接器开发人员应阅读该部分,以确定连接器可以利用可插拔认证功能的程度,以及成为更加符合规范的步骤。

如果您有兴趣编写自己的认证插件,请参阅编写认证插件。

默认认证插件

CREATE USERALTER USER语句有用于指定账户如何进行认证的语法。这种语法的某些形式没有明确指定认证插件(没有IDENTIFIED WITH子句)。例如:

CREATE USER 'jeffrey'@'localhost' IDENTIFIED BY '*password*';

在这种情况下,服务器会为账户分配默认的认证插件。在 MySQL 8.0.27 之前,这个默认值是default_authentication_plugin系统变量的值。

截至 MySQL 8.0.27,引入了多因素认证,可以有最多三个子句指定账户如何进行认证。确定没有指定插件的认证方法的默认认证插件的规则是特定于因素的:

  • 因素 1:如果authentication_policy元素 1 指定了一个认证插件,那个插件就是默认的。如果authentication_policy元素 1 是*,那么default_authentication_plugin的值就是默认的。

    根据上述规则,以下语句创建了一个双因素认证账户,第一因素认证方法由authentication_policydefault_authentication_plugin设置确定:

    CREATE USER 'wei'@'localhost' IDENTIFIED BY '*password*'
      AND IDENTIFIED WITH authentication_ldap_simple;
    

    同样地,这个示例创建了一个三因素认证账户:

    CREATE USER 'mateo'@'localhost' IDENTIFIED BY '*password*'
      AND IDENTIFIED WITH authentication_ldap_simple
      AND IDENTIFIED WITH authentication_fido;
    

    你可以使用SHOW CREATE USER来查看应用的认证方法。

  • 因素 2 或 3:如果相应的authentication_policy元素指定了一个认证插件,那个插件就是默认的。如果authentication_policy元素是*或空的,那么就没有默认值;尝试为没有指定插件的因素定义账户认证方法是一个错误,就像以下示例一样:

    mysql> CREATE USER 'sofia'@'localhost' IDENTIFIED WITH authentication_ldap_simple 
           AND IDENTIFIED BY 'abc';
    ERROR 1524 (HY000): Plugin '' is not loaded 
    mysql> CREATE USER 'sofia'@'localhost' IDENTIFIED WITH authentication_ldap_simple 
           AND IDENTIFIED BY 'abc';
    ERROR 1524 (HY000): Plugin '*' is not loaded
    

认证插件使用

本节提供了安装和使用认证插件的一般说明。有关特定插件的说明,请参阅描述该插件的部分,位于 Section 8.4.1, “Authentication Plugins”下。

一般来说,可插拔认证在服务器和客户端两侧使用一对相应的插件,因此你可以像这样使用给定的认证方法:

  • 如果需要,安装包含适当插件的插件库或插件库。在服务器主机上,安装包含服务器端插件的库,以便服务器可以用它来验证客户端连接。同样,在每个客户端主机上,安装包含客户端插件的库,供客户端程序使用。内置的认证插件无需安装。

  • 对于每个创建的 MySQL 帐户,指定要用于验证的适当服务器端插件。如果帐户要使用默认的认证插件,则帐户创建语句无需明确指定插件。服务器分配默认的认证插件,如默认认证插件中所述确定的那样。

  • 当客户端连接时,服务器端插件告诉客户端程序要使用哪个客户端插件进行验证。

如果一个帐户使用的认证方法对于服务器和客户端程序都是默认的,那么服务器无需通知客户端要使用哪个客户端插件,可以避免客户端/服务器协商中的往返。

对于标准的 MySQL 客户端,如mysqlmysqladmin,可以在命令行上指定--default-auth=*plugin_name*选项,作为程序可以期望使用的客户端插件的提示,尽管如果与用户帐户关联的服务器端插件需要不同的客户端插件,则服务器会覆盖此设置。

如果客户端程序找不到客户端插件库文件,请指定--plugin-dir=*dir_name*选项以指示插件库目录位置。

认证插件客户端/服务器兼容性

可插拔认证为 MySQL 帐户的认证方法选择提供了灵活性,但在某些情况下,由于客户端和服务器之间的认证插件不兼容,可能无法建立客户端连接。

对于成功连接到给定服务器上给定帐户的客户端的一般兼容性原则是,客户端和服务器都必须支持帐户所需的认证方法。因为认证方法由认证插件实现,所以客户端和服务器都必须支持帐户所需的认证插件

认证插件不兼容可能以各种方式出现。例如:

  • 使用 5.7.22 或更低版本的 MySQL 5.7 客户端连接到一个使用caching_sha2_password进行认证的 MySQL 8.0 服务器账户。这会失败,因为 5.7 客户端不识别这个插件,这个插件是在 MySQL 8.0 中引入的。(这个问题在 MySQL 5.7 中已经得到解决,从 5.7.23 开始,MySQL 客户端库和客户端程序添加了对caching_sha2_password的客户端支持。)

  • 使用 MySQL 5.7 客户端连接到一个使用mysql_old_password进行认证的低于 5.7 版本的服务器账户。这会因为多种原因而失败。首先,这样的连接需要--secure-auth=0,这不再是一个支持的选项。即使它被支持,5.7 客户端也不会识别这个插件,因为它在 MySQL 5.7 中被移除了。

  • 使用来自 Community 发行版的 MySQL 5.7 客户端连接到一个使用企业专用 LDAP 认证插件之一进行认证的 MySQL 5.7 企业服务器账户。这会失败,因为 Community 客户端无法访问企业插件。

一般来说,当客户端和服务器来自同一 MySQL 发行版时,这些兼容性问题不会出现。当客户端和服务器来自不同的 MySQL 系列时,问题可能会出现。当 MySQL 引入新的认证插件或移除旧插件时,这些问题是开发过程中固有的。为了最大程度地减少不兼容性的可能性,定期及时升级服务器、客户端和连接器。

认证插件连接器编写注意事项

存在多种 MySQL 客户端/服务器协议的实现。libmysqlclient C API 客户端库是其中一种实现。一些 MySQL 连接器(通常不是用 C 语言编写的)提供了它们自己的实现。然而,并非所有协议实现都以相同的方式处理插件认证。本节描述了协议实现者应该考虑的一个认证问题。

在客户端/服务器协议中,服务器会告诉连接的客户端它认为是默认的认证插件。如果客户端使用的协议实现尝试加载默认插件,而该插件在客户端上不存在,加载操作将失败。如果默认插件不是客户端尝试连接的账户实际需要的插件,这是一个不必要的失败。

如果客户端/服务器协议实现没有自己的默认认证插件概念,并始终尝试加载服务器指定的默认插件,如果该插件不可用,则会出现错误。

为了避免这个问题,客户端使用的协议实现应该有自己的默认插件,并将其作为首选(或者在无法加载服务器指定的默认插件时,回退到这个默认插件)。例如:

  • 在 MySQL 5.7 中,libmysqlclient默认选择要么是mysql_native_password,要么是通过MYSQL_DEFAULT_AUTH选项为mysql_options()指定的插件。

  • 当 5.7 客户端尝试连接到 8.0 服务器时,服务器将caching_sha2_password指定为其默认认证插件,但客户端仍然根据mysql_native_password或通过MYSQL_DEFAULT_AUTH指定的凭据详细信息发送。

  • 客户端仅在更改插件请求时加载服务器指定的插件,但在这种情况下,它可以是任何取决于用户帐户的插件。在这种情况下,客户端必须尝试加载插件,如果该插件不可用,则错误是不可选的。

可插拔认证限制

本节的第一部分描述了在第 8.2.17 节,“可插拔认证”中描述的可插拔认证框架的适用性的一般限制。第二部分描述了第三方连接器开发人员如何确定连接器可以利用可插拔认证功能的程度以及采取哪些步骤以变得更加符合规范。

此处使用的“本机认证”术语指的是针对存储在mysql.user系统表中的密码进行的认证。这是在实施可插拔认证之前由较旧的 MySQL 服务器提供的相同认证方法。“Windows 本机认证”是指使用已经登录到 Windows 的用户的凭据进行认证,由 Windows 本机认证插件(简称“Windows 插件”)实施。

  • 通用可插拔认证限制

  • 可插拔认证和第三方连接器

通用可插拔认证限制
  • Connector/C++: 使用此连接器的客户端只能通过使用本机认证的帐户连接到服务器。

    例外:如果连接器是动态链接到libmysqlclient(而不是静态链接)构建的,并且如果安装了该版本,则加载当前版本的libmysqlclient,或者如果连接器从源代码重新编译以链接到当前的libmysqlclient

    有关编写连接器以处理有关默认服务器端认证插件信息的信息,请参阅认证插件连接器编写注意事项。

  • Connector/NET: 使用 Connector/NET 的客户端可以通过使用本地认证或 Windows 本地认证的帐户连接到服务器。

  • Connector/PHP: 使用此连接器的客户端只能通过使用 MySQL 本机驱动程序(mysqlnd)编译时使用本地认证的帐户连接到服务器。

  • Windows 本地认证: 通过使用 Windows 插件的帐户连接需要 Windows 域设置。如果没有设置,将使用 NTLM 认证,然后只有本地连接是可能的;也就是说,客户端和服务器必须在同一台计算机上运行。

  • 代理用户: 代理用户支持程度取决于客户端是否可以通过使用实现代理用户功能的插件进行身份验证的帐户连接(即,可以返回与连接用户不同的用户名的插件)。例如,PAM 和 Windows 插件支持代理用户。mysql_native_passwordsha256_password认证插件默认不支持代理用户,但可以进行配置;请参阅服务器支持代理用户映射。

  • 复制: 复制副本不仅可以使用本地认证的复制用户帐户,还可以通过使用非本地认证的复制用户帐户连接,如果所需的客户端端插件可用。如果插件内置于libmysqlclient中,则默认可用。否则,插件必须安装在副本端的由副本的plugin_dir系统变量命名的目录中。

  • FEDERATED表: FEDERATED表只能通过在远程服务器上使用本地认证的帐户访问远程表。

可插拔认证和第三方连接器

第三方连接器开发人员可以使用以下准则来确定连接器是否准备好利用可插拔认证功能,并采取哪些步骤以提高兼容性:

  • 对于未进行任何更改的现有连接器,使用本地认证,使用该连接器的客户端只能通过使用本地认证的帐户连接到服务器。但是,您应该针对服务器的最新版本测试连接器,以验证这样的连接是否仍然可以正常工作。

    异常情况:如果连接器动态链接到libmysqlclient(而不是静态链接),并且加载当前安装的libmysqlclient版本,则连接器可能可以在不进行任何更改的情况下与可插拔认证一起使用。

  • 要利用可插拔认证功能,基于 libmysqlclient 的连接器应该重新链接到当前版本的 libmysqlclient。这使连接器能够支持通过现在内置到 libmysqlclient 中的客户端插件的账户进行连接(例如用于 PAM 认证所需的明文插件和用于 Windows 本地认证所需的 Windows 插件)。与当前的 libmysqlclient 链接还使连接器能够访问安装在默认 MySQL 插件目录中的客户端插件(通常是由本地服务器的 plugin_dir 系统变量的默认值命名的目录)。

    如果连接器动态链接到 libmysqlclient,必须确保在客户端主机上安装了更新版本的 libmysqlclient,并且连接器在运行时加载它。

  • 连接器支持给定认证方法的另一种方式是直接在客户端/服务器协议中实现它。Connector/NET 使用这种方法来提供对 Windows 本地认证的支持。

  • 如果连接器应该能够从与默认插件目录不同的目录加载客户端插件,那么它必须实现一些方式让客户用户指定目录。这方面的可能性包括通过命令行选项或环境变量,从中连接器可以获取目录名称。标准的 MySQL 客户端程序,如mysqlmysqladmin实现了一个 --plugin-dir 选项。另请参阅 C API 客户端插件接口。

  • 连接器对代理用户的支持取决于,正如本节前面所述的,它支持的认证方法是否允许代理用户。

8.2.18 多因素认证

原文:dev.mysql.com/doc/refman/8.0/en/multifactor-authentication.html

认证涉及一方向第二方证明其身份。多因素认证(MFA)是在认证过程中使用多个认证值(或“因素”)的方法。MFA 比单因素认证(1FA)提供更高的安全性,单因素认证仅使用一种认证方法,比如密码。MFA 可以启用额外的认证方法,比如使用多个密码进行认证,或者使用智能卡、安全密钥和生物识别读卡器进行认证。

MySQL 8.0.27 及更高版本支持多因素认证。该功能包括需要最多三个认证值的 MFA 形式。也就是说,MySQL 账户管理支持使用 2FA 或 3FA 的账户,除了现有的 1FA 支持。

当客户端尝试使用单因素账户连接到 MySQL 服务器时,服务器调用账户定义中指定的认证插件,并根据插件报告的成功或失败来接受或拒绝连接。

对于具有多个认证因素的账户,流程类似。服务器按照账户定义中列出的顺序调用认证插件。如果插件报告成功,服务器会接受连接(如果插件是最后一个),或者如果还有插件剩余,则继续调用下一个插件。如果任何插件报告失败,服务器将拒绝连接。

以下章节详细介绍了 MySQL 中的多因素认证。

  • 多因素认证支持的元素

  • 配置多因素认证策略

  • 开始使用多因素认证

多因素认证支持的元素

认证因素通常包括以下类型的信息:

  • 你知道的东西,比如秘密密码或口令。

  • 你拥有的东西,比如安全密钥或智能卡。

  • 你的生物特征;比如指纹或面部扫描。

“你所知道的”因素类型依赖于在身份验证过程的双方都保密的信息。不幸的是,秘密可能会受到威胁:有人可能看到你输入密码,或者用网络钓鱼攻击欺骗你,服务器端存储的密码可能会在安全漏洞中暴露等。使用多个密码可以提高安全性,但每个密码仍然可能会受到威胁。使用其他因素类型可以在减少威胁的情况下提高安全性。

MySQL 中多因素身份验证的实现包括以下元素:

  • authentication_policy 系统变量控制可以使用多少身份验证因素以及每个因素允许的身份验证类型。也就是说,它对于 CREATE USERALTER USER 语句在多因素身份验证方面施加了约束。

  • CREATE USERALTER USER 具有语法,允许为新帐户指定多个身份验证方法,并为现有帐户添加、修改或删除身份验证方法。如果帐户使用 2FA 或 3FA,mysql.user 系统表会在 User_attributes 列中存储有关额外身份验证因素的信息。

  • 为了启用使用需要多个密码的帐户进行 MySQL 服务器身份验证,客户端程序具有 --password1--password2--password3 选项,允许指定最多三个密码。对于使用 C API 的应用程序,mysql_options4() C API 函数的 MYSQL_OPT_USER_PASSWORD 选项可以实现相同的功能。

  • 服务器端的 authentication_fido 插件(已弃用)允许使用设备进行身份验证。这个服务器端的 FIDO 身份验证插件仅包含在 MySQL Enterprise Edition 发行版中。它不包含在 MySQL 社区发行版中。然而,客户端的 authentication_fido_client 插件(已弃用)包含在所有发行版中,包括社区发行版。这使得来自任何发行版的客户端可以连接到使用 authentication_fido 在加载了该插件的服务器上进行身份验证的帐户。参见 Section 8.4.1.11, “FIDO Pluggable Authentication”。

  • authentication_fido 还可以实现免密码认证,如果它是账户唯一使用的认证插件。查看 FIDO 免密码认证。

  • 多因素认证可以使用非 FIDO MySQL 认证方法、FIDO 认证方法或两者的组合。

  • 这些权限使用户能够执行某些受限的多因素认证相关操作:

    • 拥有AUTHENTICATION_POLICY_ADMIN 权限的用户不受authentication_policy 系统变量强加的限制。 (对于否则不允许的语句会发出警告。)

    • PASSWORDLESS_USER_ADMIN 权限允许创建免密码认证账户并对其进行操作复制。

配置多因素认证策略

authentication_policy 系统变量定义了多因素认证策略。 具体来说,它定义了账户可以拥有(或需要拥有)多少认证因素以及可以用于每个因素的认证方法。

authentication_policy 的值是一个由 1、2 或 3 个逗号分隔的元素组成的列表。 列表中的每个元素对应一个认证因素,可以是认证插件名称、星号(*)、空或缺失。 (例外:元素 1 不能是空或缺失。) 整个列表用单引号括起来。 例如,以下authentication_policy 值包括一个星号、一个认证插件名称和一个空元素:

authentication_policy = '*,authentication_fido,'

星号(*)表示需要认证方法,但允许任何方法。 空元素表示认证方法是可选的,且允许任何方法。 缺失的元素(没有星号、空元素或认证插件名称)表示不允许认证方法。 当指定插件名称时,创建或修改账户时需要该认证方法作为相应因素的必需方法。

默认authentication_policy 值为'*,,'(一个星号和两个空元素),需要第一因素,并可选择允许第二和第三因素。 因此,默认authentication_policy 值与现有的 1FA 账户向后兼容,但也允许创建或修改账户以使用 2FA 或 3FA。

拥有AUTHENTICATION_POLICY_ADMIN权限的用户不受authentication_policy设置所施加的约束。 (对于否则不允许的语句会发出警告。)

authentication_policy值可以在选项文件中定义或使用SET GLOBAL语句指定:

SET GLOBAL authentication_policy='*,*,';

有几条规则规定了如何定义authentication_policy值。请参考authentication_policy系统变量描述,了解这些规则的完整说明。以下表格提供了几个authentication_policy示例值及其所建立的策略。

表 8.11 示例 authentication_policy 值

authentication_policy 值 有效策略
'*' 仅允许创建或更改具有一个因素的帐户。
'*,*' 仅允许创建或更改具有两个因素的帐户。
'*,*,*' 仅允许创建或更改具有三个因素的帐户。
'*,' 允许创建或更改具有一或两个因素的帐户。
'*,,' 允许创建或更改具有一、两或三个因素的帐户。
'*,*,' 允许创建或更改具有两个或三个因素的帐户。
'*,*auth_plugin*' 允许创建或更改具有两个因素的帐户,其中第一个因素可以是任何认证方法,第二个因素必须是指定的插件。
'*auth_plugin*,*,' 允许创建或更改具有两个或三个因素的帐户,其中第一个因素必须是指定的插件。
'*auth_plugin*,' 允许创建或更改具有一或两个因素的帐户,其中第一个因素必须是指定的插件。
'*auth_plugin*,*auth_plugin*,*auth_plugin*' 允许创建或更改具有三个因素的帐户,其中因素必须使用指定的插件。
authentication_policy 值 有效策略

开始使用多因素认证

默认情况下,MySQL 使用一个多因素认证策略,允许任何认证插件作为第一个因素,并可选择允许第二和第三个认证因素。此策略是可配置的;有关详细信息,请参阅配置多因素认证策略。

注意

不允许使用任何内部凭据存储插件(caching_sha2_passwordmysql_native_password)作为第 2 或第 3 因素。

假设您希望一个账户首先使用 caching_sha2_password 插件进行认证,然后再使用 authentication_ldap_sasl SASL LDAP 插件进行认证。(这假设 LDAP 认证已经按照 Section 8.4.1.7, “LDAP Pluggable Authentication” 中描述的设置完成,并且用户在 LDAP 目录中有一个与示例中显示的认证字符串对应的条目。)使用以下语句创建账户:

CREATE USER 'alice'@'localhost'
  IDENTIFIED WITH caching_sha2_password
    BY '*sha2_password*'
  AND IDENTIFIED WITH authentication_ldap_sasl
    AS 'uid=u1_ldap,ou=People,dc=example,dc=com';

要连接,用户必须提供两个密码。为了启用使用需要多个密码的账户连接到 MySQL 服务器的认证,客户端程序具有 --password1--password2--password3 选项,允许指定最多三个密码。这些选项类似于 --password 选项,可以在命令行上跟随选项后面输入密码值(这是不安全的),或者如果没有密码值,则会提示用户输入密码。对于刚创建的账户,因素 1 和 2 需要密码,因此使用 --password1--password2 选项调用 mysql 客户端。mysql 依次提示输入每个密码:

$> mysql --user=alice --password1 --password2
Enter password: *(enter factor 1 password)* Enter password: *(enter factor 2 password)*

假设您想要添加第三个认证因素。可以通过删除并重新创建具有第三个因素的用户,或者使用 ALTER USER *user* ADD *factor* 语法来实现。以下两种方法都显示如下:

DROP USER 'alice'@'localhost';

CREATE USER 'alice'@'localhost'
  IDENTIFIED WITH caching_sha2_password
    BY '*sha2_password*'
  AND IDENTIFIED WITH authentication_ldap_sasl
    AS 'uid=u1_ldap,ou=People,dc=example,dc=com'
  AND IDENTIFIED WITH authentication_fido;

ADD *factor* 语法包括因素编号和 FACTOR 关键字:

ALTER USER 'alice'@'localhost' ADD 3 FACTOR IDENTIFIED WITH authentication_fido;

ALTER USER *user* DROP *factor* 语法允许删除一个因素。以下示例删除了前一个示例中添加的第三个因素(authentication_fido):

ALTER USER 'alice'@'localhost' DROP 3 FACTOR;

ALTER USER *user* MODIFY *factor* 语法允许更改特定因素的插件或认证字符串,前提是该因素存在。以下示例修改了第二个因素,将认证方法从 authentication_ldap_sasl 更改为 authetication_fido

ALTER USER 'alice'@'localhost' MODIFY 2 FACTOR IDENTIFIED WITH authentication_fido;

使用 SHOW CREATE USER 查看为账户定义的认证方法:

SHOW CREATE USER 'u1'@'localhost'\G
*************************** 1\. row ***************************
CREATE USER for u1@localhost: CREATE USER `u1`@`localhost` 
IDENTIFIED WITH 'caching_sha2_password' AS '*sha2_password*' 
AND IDENTIFIED WITH 'authentication_fido' REQUIRE NONE 
PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY 
DEFAULT PASSWORD REUSE INTERVAL DEFAULT PASSWORD REQUIRE 
CURRENT DEFAULT

8.2.19 代理用户

原文:dev.mysql.com/doc/refman/8.0/en/proxy-users.html

MySQL 服务器使用认证插件对客户端连接进行身份验证。认证给定连接的插件可能要求将连接的(外部)用户视为不同用户以进行特权检查。这使得外部用户可以成为第二用户的代理;也就是说,承担第二用户的权限:

  • 外部用户是“代理用户”(可以冒充或成为另一个用户的用户)。

  • 第二个用户是“被代理用户”(其身份和特权可以被代理用户所假定)。

本节描述了代理用户功能的工作原理。有关认证插件的一般信息,请参阅第 8.2.17 节“可插拔认证”。有关特定插件的信息,请参阅第 8.4.1 节“认证插件”。有关编写支持代理用户的认证插件的信息,请参阅在认证插件中实现代理用户支持。

  • 代理用户支持要求

  • 简单代理用户示例

  • 防止直接登录代理帐户

  • 授予和撤销 PROXY 特权

  • 默认代理用户

  • 默认代理用户和匿名用户冲突

  • 服务器支持代理用户映射

  • 代理用户系统变量

注意

代理的一个管理优势是,DBA 可以设置一个具有一组特权的单个帐户,然后启用多个代理用户具有这些特权,而无需为每个用户单独分配特权。作为代理用户的替代方案,DBA 可能会发现角色提供了一种适合将用户映射到特定命名特权集的方法。每个用户可以被授予一个给定的单一角色,以实际上被授予适当的特权集。请参阅第 8.2.10 节“使用角色”。

代理用户支持要求

要对给定认证插件进行代理,必须满足以下条件:

  • 必须支持代理,可以是插件本身支持,也可以是 MySQL 服务器代表插件支持。在后一种情况下,可能需要显式启用服务器支持;参见服务器支持代理用户映射。

  • 外部代理用户的账户必须设置为由插件进行身份验证。使用CREATE USER语句将账户与身份验证插件关联,或使用ALTER USER更改其插件。

  • 被代理用户的账户必须存在并被授予代理用户所需的权限。使用CREATE USERGRANT语句进行此操作。

  • 通常,代理用户被配置为仅在代理场景中使用,而不用于直接登录。

  • 代理用户账户必须具有PROXY权限以供被代理账户使用。使用GRANT语句进行此操作。

  • 对于连接到代理账户的客户端要被视为代理用户,身份验证插件必须返回与客户端用户名不同的用户名,以指示由代理用户承担的权限定义的被代理账户的用户名。

    或者,对于由服务器提供代理映射的插件,被代理用户是由代理用户持有的PROXY权限确定的。

代理机制仅允许将外部客户端用户名映射到被代理用户名称。没有提供主机名映射的规定:

  • 当客户端连接到服务器时,服务器根据客户端程序传递的用户名和客户端连接的主机确定适当的账户。

  • 如果该账户是代理账户,则服务器尝试通过找到与身份验证插件返回的用户名和代理账户的主机名匹配的被代理账户来确定适当的被代理账户。被代理账户中的主机名将被忽略。

简单代理用户示例

考虑以下账户定义:

-- create proxy account
CREATE USER 'employee_ext'@'localhost'
  IDENTIFIED WITH my_auth_plugin
  AS '*my_auth_string*';

-- create proxied account and grant its privileges;
-- use mysql_no_login plugin to prevent direct login
CREATE USER 'employee'@'localhost'
  IDENTIFIED WITH mysql_no_login;
GRANT ALL
  ON employees.*
  TO 'employee'@'localhost';

-- grant to proxy account the
-- PROXY privilege for proxied account
GRANT PROXY
  ON 'employee'@'localhost'
  TO 'employee_ext'@'localhost';

当客户端以employee_ext从本地主机连接时,MySQL 使用名为my_auth_plugin的插件执行身份验证。假设my_auth_plugin根据'*my_auth_string*'的内容并可能通过查询某些外部身份验证系统返回一个名为employee的用户名给服务器。名称employeeemployee_ext不同,因此返回employee作为请求服务器将employee_ext外部用户视为employee本地用户进行权限检查。

在这种情况下,employee_ext是代理用户,employee是被代理用户。

服务器通过检查employee_ext(代理用户)是否对employee(被代理用户)具有PROXY权限来验证employee的代理认证是否对employee_ext用户可行。如果未授予此权限,则会发生错误。否则,employee_ext假定employee的权限。服务器通过检查由employee_ext在客户端会话期间执行的语句来针对授予employee的权限。在这种情况下,employee_ext可以访问employees数据库中的表。

代理账户employee使用mysql_no_login认证插件来防止客户端直接使用该账户登录。(假设插件已安装。有关说明,请参见 Section 8.4.1.9, “无登录可插入式认证”。)要了解保护代理账户免受直接使用的替代方法,请参见防止直接登录到代理账户。

当进行代理时,USER()CURRENT_USER()函数可用于查看连接用户(代理用户)与当前会话期间适用的账户(被代理用户)之间的区别。对于刚才描述的示例,这些函数返回以下值:

mysql> SELECT USER(), CURRENT_USER();
+------------------------+--------------------+
| USER()                 | CURRENT_USER()     |
+------------------------+--------------------+
| employee_ext@localhost | employee@localhost |
+------------------------+--------------------+

在创建代理用户账户的CREATE USER语句中,指定支持代理的认证插件的IDENTIFIED WITH子句后面可以选择跟随一个AS '*auth_string*'子句,该子句指定服务器在用户连接时传递给插件的字符串。如果存在,该字符串提供帮助插件确定如何将代理(外部)客户端用户名映射到被代理用户名称的信息。每个插件是否需要AS子句取决于插件打算如何使用它。如果需要,认证字符串的格式取决于插件打算如何使用它。请查阅特定插件的文档,了解其接受的认证字符串值的信息。

防止直接登录到代理账户

代理账户通常只能通过代理账户使用。也就是说,客户端使用代理账户连接,然后映射到并假定适当被代理用户的权限。

有多种方法可以确保代理账户不能直接使用:

  • 将账户与 mysql_no_login 认证插件关联。在这种情况下,账户无论如何都不能用于直接登录。这假定插件已安装。有关说明,请参见 Section 8.4.1.9, “No-Login Pluggable Authentication”。

  • 创建账户时包括 ACCOUNT LOCK 选项。参见 Section 15.7.1.3, “CREATE USER Statement”。使用这种方法时,还要包括一个密码,这样如果账户稍后解锁,就不能在没有密码的情况下访问它。(如果启用了 validate_password 组件,则不允许创建没有密码的账户,即使账户被锁定。参见 Section 8.4.3, “The Password Validation Component”。)

  • 创建带有密码的账户,但不要告诉其他人密码。如果不让任何人知道账户的密码,客户端就无法使用它直接连接到 MySQL 服务器。

授予和撤销 PROXY 权限

需要 PROXY 权限以使外部用户能够连接并具有另一个用户的权限。要授予此权限,请使用 GRANT 语句。例如:

GRANT PROXY ON '*proxied_user*' TO '*proxy_user*';

该语句在 mysql.proxies_priv 授权表中创建一行。

在连接时,proxy_user 必须代表一个有效的外部认证的 MySQL 用户,而 proxied_user 必须代表一个有效的本地认证用户。否则,连接尝试将失败。

相应的 REVOKE 语法为:

REVOKE PROXY ON '*proxied_user*' FROM '*proxy_user*';

MySQL GRANTREVOKE 语法扩展与往常一样工作。示例:

-- grant PROXY to multiple accounts
GRANT PROXY ON 'a' TO 'b', 'c', 'd';

-- revoke PROXY from multiple accounts
REVOKE PROXY ON 'a' FROM 'b', 'c', 'd';

-- grant PROXY to an account and enable the account to grant
-- PROXY to the proxied account
GRANT PROXY ON 'a' TO 'd' WITH GRANT OPTION;

-- grant PROXY to default proxy account
GRANT PROXY ON 'a' TO ''@'';

可以在以下情况下授予 PROXY 权限:

  • 由具有 proxied_userGRANT PROXY ... WITH GRANT OPTION 权限的用户。

  • proxied_user 为自身:USER() 的值必须与 CURRENT_USER()proxied_user 完全匹配,包括账户名和主机名部分。

在 MySQL 安装期间创建的初始 root 账户具有 PROXY ... WITH GRANT OPTION 权限,适用于 ''@'',即所有用户和所有主机。这使得 root 能够设置代理用户,并授权其他账户设置代理用户。例如,root 可以这样做:

CREATE USER 'admin'@'localhost'
  IDENTIFIED BY '*admin_password*';
GRANT PROXY
  ON ''@''
  TO 'admin'@'localhost'
  WITH GRANT OPTION;

这些语句创建一个可以管理所有 GRANT PROXY 映射的 admin 用��。例如,admin 可以这样做:

GRANT PROXY ON sally TO joe;

默认代理用户

要指定某些或所有用户应使用特定的身份验证插件连接,请创建一个“空白”MySQL 帐户,具有空用户名和主机名(''@''),将其与该插件关联,并让插件返回真实的经过身份验证的用户名(如果与空白用户不同)。假设存在一个名为ldap_auth的插件,实现 LDAP 身份验证并将连接用户映射到开发者或经理帐户。要设置用户代理到这些帐户,使用以下语句:

-- create default proxy account
CREATE USER ''@''
  IDENTIFIED WITH ldap_auth
  AS 'O=Oracle, OU=MySQL';

-- create proxied accounts; use
-- mysql_no_login plugin to prevent direct login
CREATE USER 'developer'@'localhost'
  IDENTIFIED WITH mysql_no_login;
CREATE USER 'manager'@'localhost'
  IDENTIFIED WITH mysql_no_login;

-- grant to default proxy account the
-- PROXY privilege for proxied accounts
GRANT PROXY
  ON 'manager'@'localhost'
  TO ''@'';
GRANT PROXY
  ON 'developer'@'localhost'
  TO ''@'';

现在假设一个客户端连接如下:

$> mysql --user=myuser --password ...
Enter password: *myuser_password*

服务器未找到定义为 MySQL 用户的myuser,但是因为存在一个空用户帐户(''@'')与客户端用户名和主机名匹配,服务器会根据该帐户对客户端进行身份验证。服务器调用ldap_auth身份验证插件,并将myusermyuser_password作为用户名和密码传递给它。

如果ldap_auth插件在 LDAP 目录中发现myuser_password不是myuser的正确密码,身份验证失败,服务器拒绝连接。

如果密码正确,并且ldap_auth发现myuser是一个开发者,它会将用户名developer返回给 MySQL 服务器,而不是myuser。返回与客户端用户名myuser不同的用户名向服务器发出信号,表明它应该将myuser视为代理。服务器验证''@''是否可以作为developer进行身份验证(因为''@''具有PROXY权限),并接受连接。会话继续进行,myuser具有developer代理用户的权限。(这些权限应由 DBA 使用GRANT语句设置,未显示。)USER()CURRENT_USER()函数返回这些值:

mysql> SELECT USER(), CURRENT_USER();
+------------------+---------------------+
| USER()           | CURRENT_USER()      |
+------------------+---------------------+
| myuser@localhost | developer@localhost |
+------------------+---------------------+

如果插件在 LDAP 目录中发现myuser是一个经理,它会将manager作为用户名返回,并且会话将继续进行,myuser具有manager代理用户的权限。

mysql> SELECT USER(), CURRENT_USER();
+------------------+-------------------+
| USER()           | CURRENT_USER()    |
+------------------+-------------------+
| myuser@localhost | manager@localhost |
+------------------+-------------------+

为简单起见,外部身份验证不能是多级的:在上述示例中,developermanager的凭据都不会被考虑。但是,如果客户端尝试直接连接并作为developermanager帐户进行身份验证,仍然会使用这些帐户,因此应该保护这些代理帐户免受直接登录的影响(请参阅防止直接登录到代理帐户)。

默认代理用户和匿名用户冲突

如果您打算创建一个默认代理用户,请检查其他现有的“匹配任何用户”帐户,因为它们可能优先于默认代理用户,从而阻止该用户按预期工作。

在前面的讨论中,默认代理用户账户在主机部分有'',匹配任何主机。如果设置了默认代理用户,请务必检查是否存在具有相同用户部分和'%'主机部分的非代理账户,因为'%'也匹配任何主机,但根据服务器用于内部排序账户行的规则,'%'优先于''(请参阅第 8.2.6 节,“访问控制,阶段 1:连接验证”)。

假设 MySQL 安装包括这两个账户:

-- create default proxy account
CREATE USER ''@''
  IDENTIFIED WITH some_plugin
  AS '*some_auth_string*';
-- create anonymous account
CREATE USER ''@'%'
  IDENTIFIED BY '*anon_user_password*';

第一个账户(''@'')旨在作为默认代理用户,用于验证那些不匹配更具体账户的用户的连接。第二个账户(''@'%')是一个匿名用户账户,例如可能已创建,以便使没有自己账户的用户能够匿名连接。

两个账户的用户部分('')相同,匹配任何用户。每个账户都有一个匹配任何主机的主机部分。然而,在连接尝试的账户匹配中存在优先级,因为匹配规则将'%'的主机排在''之前。对于不再匹配任何更具体账户的账户,服务器会尝试对其进行''@'%'(匿名用户)的身份验证,而不是''@''(默认代理用户)。因此,默认代理账户永远不会被使用。

为避免此问题,请使用以下策略之一:

  • 删除匿名账户,以避免与默认代理用户冲突。

  • 使用匹配在匿名用户之前的更具体默认代理用户。例如,为了仅允许localhost代理连接,使用''@'localhost'

    CREATE USER ''@'localhost'
      IDENTIFIED WITH some_plugin
      AS '*some_auth_string*';
    

    此外,修改任何GRANT PROXY语句,将''@'localhost'命名为代理用户,而不是''@''

    请注意,此策略会阻止来自localhost的匿名用户连接。

  • 使用具名的默认账户而不是匿名的默认账户。有关此技术的示例,请参考使用authentication_windows插件的说明。请参阅第 8.4.1.6 节,“Windows 可插拔认证”。

  • 创建多个代理用户,一个用于本地连接,另一个用于“其他所有”(远程连接)。这在本地用户应该具有不同权限于远程用户时特别有用。

    创建代理用户:

    -- create proxy user for local connections
    CREATE USER ''@'localhost'
      IDENTIFIED WITH some_plugin
      AS '*some_auth_string*';
    -- create proxy user for remote connections
    CREATE USER ''@'%'
      IDENTIFIED WITH some_plugin
      AS '*some_auth_string*';
    

    创建被代理用户:

    -- create proxied user for local connections
    CREATE USER 'developer'@'localhost'
      IDENTIFIED WITH mysql_no_login;
    -- create proxied user for remote connections
    CREATE USER 'developer'@'%'
      IDENTIFIED WITH mysql_no_login;
    

    为每个代理账户授予对应被代理账户的PROXY权限:

    GRANT PROXY
      ON 'developer'@'localhost'
      TO ''@'localhost';
    GRANT PROXY
      ON 'developer'@'%'
      TO ''@'%';
    

    最后,为本地和远程被代理用户授予适当的权限(未显示)。

    假设 some_plugin/'*some_auth_string*' 组合导致 some_plugin 将客户端用户名映射到 developer。本地连接匹配 ''@'localhost' 代理用户,映射到 'developer'@'localhost' 被代理用户。远程连接匹配 ''@'%' 代理用户,映射到 'developer'@'%' 被代理用户。

服务器对代理用户映射的支持

一些认证插件为自身实现了代理用户映射(例如,PAM 和 Windows 认证插件)。其他认证插件默认不支持代理用户。其中一些可以请求 MySQL 服务器根据授予的代理权限映射代理用户:mysql_native_passwordsha256_password。如果启用了 check_proxy_users 系统变量,服务器将为任何请求进行代理用户映射的认证插件执行代理用户映射:

  • 默认情况下,check_proxy_users 处于禁用状态,因此服务器即使对请求服务器支持代理用户的认证插件也不执行代理用户映射。

  • 如果启用了 check_proxy_users,可能还需要启用特定于插件的系统变量以利用服务器代理用户映射支持:

    • 对于 mysql_native_password 插件,请启用 mysql_native_password_proxy_users

    • 对于 sha256_password 插件,请启用 sha256_password_proxy_users

例如,要启用所有上述功能,请在 my.cnf 文件中添加以下行启动服务器:

[mysqld]
check_proxy_users=ON
mysql_native_password_proxy_users=ON
sha256_password_proxy_users=ON

假设相关系统变量已启用,请像往常一样使用 CREATE USER 创建代理用户,然后授予它对一个其他账户的 PROXY 权限,以将其视为被代理用户。当服务器接收到代理用户的成功连接请求时,发现用户具有 PROXY 权限,并使用它确定适当的被代理用户。

-- create proxy account
CREATE USER 'proxy_user'@'localhost'
  IDENTIFIED WITH mysql_native_password
  BY '*password*';

-- create proxied account and grant its privileges;
-- use mysql_no_login plugin to prevent direct login
CREATE USER 'proxied_user'@'localhost'
  IDENTIFIED WITH mysql_no_login;
-- grant privileges to proxied account
GRANT ...
  ON ...
  TO 'proxied_user'@'localhost';

-- grant to proxy account the
-- PROXY privilege for proxied account
GRANT PROXY
  ON 'proxied_user'@'localhost'
  TO 'proxy_user'@'localhost';

要使用代理账户,请使用其名称和密码连接到服务器:

$> mysql -u proxy_user -p
Enter password: *(enter proxy_user password here)*

认证成功后,服务器发现 proxy_userproxied_userPROXY 权限,会话将继续进行,proxy_user 具有 proxied_user 的权限。

服务器执行的代理用户映射受到以下限制:

  • 服务器不会代理匿名用户,即使授予了相关的 PROXY 权限。

  • 当一个单一账户被授予多个被代理账户的代理权限时,服务器代理用户映射是不确定的。因此,不建议为单一账户授予多个被代理账户的代理权限。

代理用户系统变量

有两个系统变量帮助跟踪代理登录过程:

  • proxy_user:如果未使用代理,则该值为NULL。否则,它表示代理用户账户。例如,如果客户端通过''@''代理账户进行身份验证,那么该变量将设置如下:

    mysql> SELECT @@proxy_user;
    +--------------+
    | @@proxy_user |
    +--------------+
    | ''@''        |
    +--------------+
    
  • external_user:有时认证插件可能使用外部用户来认证到 MySQL 服务器。例如,当使用 Windows 本地认证时,一个使用 Windows API 进行认证的插件不需要传递登录 ID。然而,它仍然使用 Windows 用户 ID 进行认证。插件可能会将这个外部用户 ID(或其前 512 个 UTF-8 字节)返回给服务器,使用external_user只读会话变量。如果插件没有设置这个变量,其值为NULL

8.2.20 账户锁定

原文:dev.mysql.com/doc/refman/8.0/en/account-locking.html

MySQL 支持使用ACCOUNT LOCKACCOUNT UNLOCK子句对用户账户进行锁定和解锁,用于CREATE USERALTER USER语句:

  • 在与CREATE USER一起使用时,这些子句指定新账户的初始锁定状态。如果没有任何子句,则账户将以未锁定状态创建。

    如果启用了validate_password组件,则不允许创建没有密码的账户,即使该账户被锁定。参见第 8.4.3 节,“密码验证组件”。

  • 在与ALTER USER一起使用时,这些子句指定现有账户的新锁定状态。如果没有任何子句,��账户的锁定状态保持不变。

    从 MySQL 8.0.19 开始,ALTER USER ... UNLOCK解锁由于登录失败次数过多而暂时锁定的任何账户。参见第 8.2.15 节,“密码管理”。

账户锁定状态记录在mysql.user系统表的account_locked列中。SHOW CREATE USER的输出指示账户是锁定还是未锁定。

如果客户端尝试连接到一个被锁定的账户,连接尝试将失败。服务器会增加Locked_connects状态变量,指示尝试连接到被锁定账户的次数,返回一个ER_ACCOUNT_HAS_BEEN_LOCKED错误,并在错误日志中写入一条消息:

Access denied for user '*user_name*'@'*host_name*'.
Account is locked.

锁定账户不影响使用代理用户连接的能力,代理用户假定被锁定账户的身份。它也不影响执行具有指定被锁定账户的DEFINER属性的存储程序或视图的能力。也就是说,锁定账户不影响使用代理账户或存储程序或视图的能力。

账户锁定功能取决于mysql.user系统表中是否存在account_locked列。对于从 MySQL 版本旧于 5.7.6 的升级,请执行 MySQL 升级过程以确保该列存在。参见第三章,“升级 MySQL”。对于没有account_locked列的非升级安装,服务器将所有账户视为未锁定,使用ACCOUNT LOCKACCOUNT UNLOCK子句会产生错误。

8.2.21 设置帐户资源限制

原文:dev.mysql.com/doc/refman/8.0/en/user-resources.html

限制客户端使用 MySQL 服务器资源的一种方法是将全局max_user_connections系统变量设置为非零值。这限制了任何给定帐户可以建立的同时连接数,但不限制客户端连接后可以做的事情。此外,设置max_user_connections不会启用对个别帐户的管理。这两种类型的控制对 MySQL 管理员很重要。

为了解决这些问题,MySQL 允许对个别帐户使用这些服务器资源的限制:

  • 一个帐户每小时可以发出的查询次数

  • 一个帐户每小时可以发出的更新次数

  • 一个帐户每小时可以连接到服务器的次数

  • 一个帐户对服务器的同时连接数

客户端可以发出的任何语句都计入查询限制。只有修改数据库或表的语句才计入更新限制。

在这种情况下,“帐户”对应于mysql.user系统表中的一行。也就是说,连接根据适用于连接的user表行中的UserHost值进行评估。例如,帐户'usera'@'%.example.com'对应于user表中具有UserHost值为usera%.example.com的行,以允许useraexample.com域中的任何主机连接。在这种情况下,服务器将在此行中的资源限制集体应用于useraexample.com域中的任何主机发起的所有连接,因为所有这样的连接使用相同的帐户。

在 MySQL 5.0 之前,“帐户”是根据用户连接的实际主机进行评估的。可以通过使用--old-style-user-limits选项启动服务器来选择这种较旧的计算方法。在这种情况下,如果usera同时从host1.example.comhost2.example.com连接,服务器会将帐户资源限制分别应用于每个连接。如果usera再次从host1.example.com连接,服务器将与该主机的现有连接一起应用该连接的限制。

注意

在 MySQL 8.0.30 中,--old-style-user-limits选项已被弃用,并可能在将来的 MySQL 版本中被移除。在 MySQL 8.0.30 或更高版本中在命令行或选项文件中使用此选项会导致服务器发出警告。

要在创建帐户时为帐户建立资源限制,请使用CREATE USER语句。要修改现有帐户的限制,请使用ALTER USER。提供一个WITH子句,命名要限制的每个资源。每个限制的默认值为零(无限制)。例如,要创建一个新帐户,可以访问customer数据库,但只能以有限的方式访问,发出以下语句:

mysql> CREATE USER 'francis'@'localhost' IDENTIFIED BY 'frank'
 ->     WITH MAX_QUERIES_PER_HOUR 20
 ->          MAX_UPDATES_PER_HOUR 10
 ->          MAX_CONNECTIONS_PER_HOUR 5
 ->          MAX_USER_CONNECTIONS 2;

限制类型不需要全部在WITH子句中命名,但命名的限制可以以任何顺序出现。每小时限制的值应为表示每小时计数的整数。对于MAX_USER_CONNECTIONS,限制是表示帐户同时连接数的整数。如果将此限制设置为零,则全局max_user_connections系统变量值确定同时连接数。如果max_user_connections也为零,则该帐户没有限制。

要修改现有帐户的限制,请使用ALTER USER。以下语句将francis的查询限制更改为 100:

mysql> ALTER USER 'francis'@'localhost' WITH MAX_QUERIES_PER_HOUR 100;

该语句仅修改指定的限制值,不会对帐户进行其他更改。

要删除限制,请将其值设置为零。例如,要删除francis每小时连接次数限制,使用以下语句:

mysql> ALTER USER 'francis'@'localhost' WITH MAX_CONNECTIONS_PER_HOUR 0;

如前所述,帐户的同时连接限制是根据MAX_USER_CONNECTIONS限制和max_user_connections系统变量确定的。假设全局max_user_connections值为 10,并且有三个帐户具有以下指定的各自资源限制:

ALTER USER 'user1'@'localhost' WITH MAX_USER_CONNECTIONS 0;
ALTER USER 'user2'@'localhost' WITH MAX_USER_CONNECTIONS 5;
ALTER USER 'user3'@'localhost' WITH MAX_USER_CONNECTIONS 20;

user1的连接限制为 10(全局max_user_connections值),因为它的MAX_USER_CONNECTIONS限制为零。user2user3的连接限制分别为 5 和 20,因为它们具有非零的MAX_USER_CONNECTIONS限制。

服务器将帐户的资源限制存储在与帐户对应的user表行中。max_questionsmax_updatesmax_connections列存储每小时限制,max_user_connections列存储MAX_USER_CONNECTIONS限制。(参见第 8.2.3 节,“授权表”。)

当任何帐户对其任何资源的使用放置了非零限制时,资源使用计数会发生。

当服务器运行时,它会计算每个账户使用资源的次数。如果一个账户在最近一小时内达到了连接数限制,服务器将拒绝该账户的进一步连接,直到该小时结束。同样,如果账户达到了查询或更新次数限制,服务器将拒绝进一步的查询或更新,直到该小时结束。在所有这些情况下,服务器会发出适当的错误消息。

资源计数是按账户而不是按客户端进行的。例如,如果您的账户有一个查询限制为 50,您不能通过同时在服务器上建立两个客户端连接来将限制增加到 100。两个连接上发出的查询会被一起计算。

当前每小时资源使用计数可以全局重置所有账户,或者针对特定账户进行单独重置:

  • 要将当前计数重置为零以适用于所有账户,请发出一个FLUSH USER_RESOURCES语句。也可以通过重新加载授权表(例如,使用FLUSH PRIVILEGES语句或mysqladmin reload命令)来重置计数。

  • 可以通过再次设置任何限制值将单个账户的计数重置为零。指定一个与当前分配给账户的值相等的限制值。

每小时计数重置不会影响MAX_USER_CONNECTIONS限制。

所有计数在服务器启动时都从零开始。计数不会在服务器重新启动时保留。

对于MAX_USER_CONNECTIONS限制,如果账户当前已经打开了允许的最大连接数,则可能出现一个边界情况:快速断开连接然后立即重新连接可能会导致错误(ER_TOO_MANY_USER_CONNECTIONSER_USER_LIMIT_REACHED),如果服务器在重新连接发生时还没有完全处理断开连接。当服务器完成断开连接处理后,又可以再次允许另一个连接。

8.2.22 连接到 MySQL 时出现问题的故障排除

原文:dev.mysql.com/doc/refman/8.0/en/problems-connecting.html

如果尝试连接到 MySQL 服务器时遇到问题,以下内容描述了您可以采取的一些措施来纠正问题。

  • 确保服务器正在运行。如果没有运行,客户端无法连接到它。例如,如果尝试连接到服务器失败,并显示以下消息之一,可能的原因之一是服务器未运行:

    $> mysql
    ERROR 2003: Can't connect to MySQL server on '*host_name*' (111)
    $> mysql
    ERROR 2002: Can't connect to local MySQL server through socket
    '/tmp/mysql.sock' (111)
    
  • 可能是服务器正在运行,但您尝试使用与服务器监听的 TCP/IP 端口、命名管道或 Unix 套接字文件不同的端口。当您调用客户端程序时,指定--port选项以指示正确的端口号,或者指定--socket选项以指示正确的命名管道或 Unix 套接字文件。要找出套接字文件的位置,您可以使用以下命令:

    $> netstat -ln | grep mysql
    
  • 确保服务器未配置为忽略网络连接,或者(如果您尝试远程连接)未配置为仅在其网络接口上本地监听。如果服务器启用了skip_networking系统变量,将不接受 TCP/IP 连接。如果服务器启用了bind_address系统变量设置为127.0.0.1,它只在回环接口上本地监听 TCP/IP 连接,不接受远程连接。

  • 检查确保没有防火墙阻止访问 MySQL。您的防火墙可能根据正在执行的应用程序或 MySQL 用于通信的端口号(默认为 3306)进行配置。在 Linux 或 Unix 下,请检查您的 IP 表(或类似)配置,确保端口未被阻止。在 Windows 下,诸如 ZoneAlarm 或 Windows 防火墙等应用程序可能需要配置不要阻止 MySQL 端口。

  • 授予权限表必须正确设置,以便服务器可以使用它们进行访问控制。对于某些发行版类型(例如 Windows 上的二进制发行版,或 Linux 上的 RPM 和 DEB 发行版),安装过程会初始化 MySQL 数据目录,包括包含授予权限表的mysql系统数据库。对于不执行此操作的发行版,您必须手动初始化数据目录。有关详细信息,请参见第 2.9 节,“安装后设置和测试”。

    要确定是否需要初始化授权表,请查找数据目录下是否有一个mysql目录。(数据目录通常命名为datavar,位于您的 MySQL 安装目录下。)确保在mysql数据库目录中有一个名为user.MYD的文件。如果没有,请初始化数据目录。完成后启动服务器,您应该能够连接到服务器。

  • 在全新安装后,如果尝试以不使用密码的方式作为root登录到服务器,则可能会收到以下错���消息。

    $> mysql -u root 
    ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
    

    这意味着在安装过程中已经分配了一个 root 密码,并且必须提供该密码。请查看 Section 2.9.4, “Securing the Initial MySQL Account”,了解密码可能是如何分配的,以及在某些情况下如何找到它。如果需要重置 root 密码,请查看 Section B.3.3.2, “How to Reset the Root Password”中的说明。找到或重置密码后,请再次以root身份登录,使用--password(或-p)选项:

    $> mysql -u root -p
    Enter password:
    

    然而,如果您使用mysqld --initialize-insecure初始化 MySQL(详见 Section 2.9.1, “Initializing the Data Directory”),服务器将允许您以root身份连接而无需密码。这是一个安全风险,因此您应该为root账户设置密码;请参阅 Section 2.9.4, “Securing the Initial MySQL Account”获取指导。

  • 如果您将现有的 MySQL 安装升级到新版本,您是否执行了 MySQL 升级过程?如果没有,请执行。当添加新功能时,授权表的结构偶尔会发生变化,因此在升级后,您应始终确保您的表具有当前结构。有关说明,请参见 Chapter 3, Upgrading MySQL

  • 如果客户端程序在尝试连接时收到以下错误消息,则表示服务器期望密码的格式比客户端能够生成的格式更新:

    $> mysql
    Client does not support authentication protocol requested
    by server; consider upgrading MySQL client
    
  • 客户端程序使用在选项文件或环境变量中指定的连接参数。如果客户端程序在没有在命令行上指定默认连接参数时似乎发送了不正确的连接参数,请检查任何适用的选项文件和您的环境。例如,如果在运行一个没有任何选项的客户端时收到Access denied,请确保您没有在任何选项文件中指定旧密码!

    您可以通过使用--no-defaults选项调用客户端程序来抑制选项文件的使用。例如:

    $> mysqladmin --no-defaults -u root version
    

    客户端使用的选项文件列在第 6.2.2.2 节,“使用选项文件”中。环境变量列在第 6.9 节,“环境变量”中。

  • 如果收到以下错误,则表示您正在使用不正确的root密码:

    $> mysqladmin -u root -p*xxxx* ver
    Access denied for user 'root'@'localhost' (using password: YES)
    

    即使您没有指定密码,但出现上述错误,这意味着某个选项文件中列出了不正确的密码。尝试如前一项所述的--no-defaults选项。

    更改密码的信息,请参阅第 8.2.14 节,“分配帐户密码”。

    如果您忘记或丢失了root密码,请参阅第 B.3.3.2 节,“如何重置根密码”。

  • localhost是您的本地主机名的同义词,也是客户端尝试连接的默认主机,如果您未明确指定主机。

    您可以使用--host=127.0.0.1选项显式命名服务器主机。这将导致与本地mysqld服务器的 TCP/IP 连接。您还可以通过指定使用实际主机名的--host选项来使用 TCP/IP。在这种情况下,即使您在同一主机上运行客户端程序,主机名也必须在服务器主机上的user表行中指定。

  • “拒绝访问”错误消息告诉您尝试登录的用户、您尝试连接的客户端主机以及是否使用了密码。通常,您应该在user表中有一行与错误消息中给定的主机名和用户名完全匹配。例如,如果收到包含using password: NO的错误消息,则表示您尝试在没有密码的情况下登录。

  • 如果尝试使用mysql -u *user_name*连接到数据库时出现“拒绝访问”错误,则可能存在user表的问题。通过执行mysql -u root mysql并发出以下 SQL 语句来检查:

    SELECT * FROM user;
    

    结果应包括HostUser列与您的客户端主机名和 MySQL 用户名匹配的行。

  • 如果在尝试从 MySQL 服务器运行的主机之外的主机连接时出现以下错误,则表示user表中没有Host值与客户端主机匹配的行:

    Host ... is not allowed to connect to this MySQL server
    

    您可以通过为尝试连接时使用的客户端主机名和用户名设置一个帐户来解决此问题。

    如果您不知道正在连接的机器的 IP 地址或主机名,应在user表中将Host列值设置为'%'。尝试从客户端机器连接后,使用SELECT USER()查询查看您实际连接的方式。然后将user表中的'%'更改为日志中显示的实际主机名。否则,您的系统将因为允许给定用户名的任何主机连接而变得不安全。

    在 Linux 上,出现此错误的另一个原因可能是您使用的二进制 MySQL 版本是使用与您使用的glibc库不同版本编译的。在这种情况下,您应该升级操作系统或glibc,或者下载 MySQL 版本的源代码分发并自行编译。源 RPM 通常很容易编译和安装,因此这不是一个大问题。

  • 如果尝试连接时指定了主机名,但出现未显示主机名或为 IP 地址的错误消息,则表示 MySQL 服务器在尝试将客户端主机的 IP 地址解析为名称时出现错误:

    $> mysqladmin -u root -p*xxxx* -h *some_hostname* ver
    Access denied for user 'root'@'' (using password: YES)
    

    如果尝试以root身份连接并收到以下错误,则表示您在user表中没有一个User列值为'root'的行,而mysqld无法解析客户端的主机名:

    Access denied for user ''@'unknown'
    

    这些错误指示 DNS 问题。要修复它,请执行mysqladmin flush-hosts以重置内部 DNS 主机缓存。请参见第 7.1.12.3 节,“DNS 查找和主机缓存”。

    一些永久解决方案包括:

    • 确定您的 DNS 服务器出了什么问题并加以修复。

    • 在 MySQL 授权表中指定 IP 地址而不是主机名。

    • 在 Unix 的/etc/hosts或 Windows 的\windows\hosts中为客户端机器名称添加条目。

    • 使用启用了skip_name_resolve系统变量启动mysqld

    • 使用--skip-host-cache选项启动mysqld

    • 在 Unix 上,如果服务器和客户端在同一台机器上运行,请连接到localhost。对于连接到localhost的连接,MySQL 程序尝试使用 Unix 套接字文件连接到本地服务器,除非指定了连接参数以确保客户端进行 TCP/IP 连接。有关更多信息,请参见第 6.2.4 节,“使用命令选项连接到 MySQL 服务器”。

    • 在 Windows 上,如果您在同一台机器上运行服务器和客户端,并且服务器支持命名管道连接,请连接到主机名.(句点)。连接到.使用命名管道而不是 TCP/IP。

  • 如果mysql -u root有效,但mysql -h *your_hostname* -u root导致拒绝访问(其中your_hostname是本地主机的实际主机名),则可能在user表中没有为您的主机指定正确的名称。这里一个常见的问题是user表行中的Host值指定了一个未经验证的主机名,但您系统的名称解析例程返回了一个完全限定的域名(或反之)。例如,如果user表中有一个带有主机'pluto'的行,但您的 DNS 告诉 MySQL 您的主机名是'pluto.example.com',那么该行将无效。尝试向user表中添加一行,其中包含您主机的 IP 地址作为Host列值。(或者,您可以向user表中添加一个包含通配符的Host值的行(例如,'pluto.%')。但是,使用以%结尾的Host值是不安全的,建议使用!)

  • 如果mysql -u *user_name*有效,但mysql -u *user_name* *some_db*无效,则您尚未为名为some_db的数据库授予给定用户的访问权限。

  • 如果在服务器主机上执行mysql -u *user_name*有效,但在远程客户端主机上执行mysql -h *host_name* -u *user_name*无效,则您尚未为给定用户从远程主机启用对服务器的访问。

  • 如果你无法弄清楚为什么会出现拒绝访问的情况,请从user表中删除所有Host值包含通配符(包含'%''_'字符)的行。一个非常常见的错误是插入一个新行,其中Host='%'User='*some_user*',认为这样可以允许你指定localhost从同一台机器连接。这样做不起作用的原因是默认权限包括一行Host='localhost'User=''。因为该行具有比'%'更具体的Host'localhost',所以在从localhost连接时会优先使用该行而不是新行!正确的做法是插入第二行,其中Host='localhost'User='*some_user*',或者删除Host='localhost'User=''的行。删除行后,请记得发出FLUSH PRIVILEGES语句以重新加载授权表。另请参阅 Section 8.2.6,“访问控制,阶段 1:连接验证”。

  • 如果您能够连接到 MySQL 服务器,但在执行 SELECT ... INTO OUTFILELOAD DATA 语句时收到 Access denied 消息,那么您在 user 表中的行没有启用 FILE 权限。

  • 如果您直接更改授权表(例如,使用 INSERTUPDATEDELETE 语句),而您的更改似乎被忽略,请记住您必须执行 FLUSH PRIVILEGES 语句或 mysqladmin flush-privileges 命令,以使服务器重新加载权限表。否则,您的更改在下次服务器重启之前不会生效。请记住,在使用 UPDATE 语句更改 root 密码后,直到刷新权限之前,您不需要指定新密码,因为服务器在那时还不知道您已更改密码。

  • 如果您在会话中间发现权限似乎已更改,可能是 MySQL 管理员已更改了它们。重新加载授权表会影响新的客户端连接,但也会影响现有连接,如 第 8.2.13 节,“权限更改何时生效” 所示。

  • 如果您在 Perl、PHP、Python 或 ODBC 程序中遇到访问问题,请尝试使用 mysql -u *user_name* *db_name*mysql -u *user_name* -p*password* *db_name* 连接到服务器。如果您能够使用 mysql 客户端连接,问题可能出在您的程序上,而不是访问权限上。(-p 和密码之间没有空格;您也可以使用 --password=*password* 语法来指定密码。如果您使用 -p--password 选项而没有密码值,MySQL 会提示您输入密码。)

  • 为了测试目的,使用--skip-grant-tables选项启动mysqld服务器。然后您可以更改 MySQL 授权表,并使用SHOW GRANTS语句检查您的修改是否产生了预期效果。当您满意您的更改时,执行mysqladmin flush-privileges告诉mysqld服务器重新加载权限。这使您可以开始使用新的授权表内容,而无需停止和重新启动服务器。

  • 如果一切都失败了,请使用调试选项(例如,--debug=d,general,query)启动mysqld服务器。这将打印有关尝试连接的主机和用户信息,以及有关每个发出的命令的信息。请参阅 Section 7.9.4, “The DBUG Package”。

  • 如果您在 MySQL 授权表上遇到任何其他问题,并在MySQL 社区 Slack上提问,请始终提供 MySQL 授权表的转储。您可以使用mysqldump mysql命令转储表格。要提交错误报告,请参阅 Section 1.5, “How to Report Bugs or Problems”中的说明。在某些情况下,您可能需要使用--skip-grant-tables重新启动mysqld以运行mysqldump

8.2.23 基于 SQL 的账户活动审计

原文:dev.mysql.com/doc/refman/8.0/en/account-activity-auditing.html

应用程序可以使用以下准则执行基于 SQL 的审计,将数据库活动与 MySQL 账户关联起来。

MySQL 账户对应于mysql.user系统表中的行。当客户端成功连接时,服务器会将客户端验证为此表中的特定行。此行中的UserHost列值唯一标识账户,并对应于 SQL 语句中写入账户名称的'*user_name*'@'*host_name*'格式。

用于验证客户端的账户决定客户端拥有哪些权限。通常,可以调用CURRENT_USER()函数来确定客户端用户的账户是什么。其值是从账户的user表行的UserHost列构造的。

然而,在某些情况下,CURRENT_USER()值不对应于客户端用户,而对应于不同的账户。这发生在不基于客户端账户进行权限检查的情况下:

  • 使用SQL SECURITY DEFINER特性定义的存储过程(过程和函数)

  • 使用SQL SECURITY DEFINER特性定义的视图

  • 触发器和事件

在这些情境中,权限检查针对DEFINER账户进行,而CURRENT_USER()指的是该账户,而不是调用存储过程或视图的客户端账户或导致触发器激活的账户。要确定调用用户,可以调用USER()函数,该函数返回一个值,指示客户端提供的实际用户名和客户端连接的主机。然而,这个值不一定直接对应user表中的账户,因为USER()值从不包含通配符,而账户值(由CURRENT_USER()返回)可能包含用户名和主机名通配符。

例如,空用户名称匹配任何用户,因此''@'localhost'账户允许客户端以任何用户名作为本地主机的匿名用户连接。在这种情况下,如果客户端以本地主机的user1连接,USER()CURRENT_USER()返回不同的值:

mysql> SELECT USER(), CURRENT_USER();
+-----------------+----------------+
| USER()          | CURRENT_USER() |
+-----------------+----------------+
| user1@localhost | @localhost     |
+-----------------+----------------+

账户的主机名部分也可以包含通配符。如果主机名包含'%''_'模式字符或使用网络掩码表示法,该账户可以用于从多个主机连接的客户端,并且CURRENT_USER()值不会指示哪一个。例如,账户'user2'@'%.example.com'可以被user2用于从example.com域中的任何主机连接。如果user2remote.example.com连接,USER()CURRENT_USER()返回不同的值:

mysql> SELECT USER(), CURRENT_USER();
+--------------------------+---------------------+
| USER()                   | CURRENT_USER()      |
+--------------------------+---------------------+
| user2@remote.example.com | user2@%.example.com |
+--------------------------+---------------------+

如果应用程序必须调用USER()进行用户审计(例如,如果它在触发器内部进行审计),但必须能够将USER()值与user表中的账户关联起来,则必须避免在UserHost列中包含通配符的账户。具体来说,不要允许User为空(这会创建一个匿名用户账户),也不要在Host值中允许模式字符或网络掩码表示法。所有账户必须具有非空的User值和字面的Host值。

关于前面的例子,''@'localhost''user2'@'%.example.com'账户应该被修改,不要使用通配符:

RENAME USER ''@'localhost' TO 'user1'@'localhost';
RENAME USER 'user2'@'%.example.com' TO 'user2'@'remote.example.com';

如果user2必须能够从example.com域中的多个主机连接,应该为每个主机单独创建一个账户。

CURRENT_USER()USER()值中提取用户名称或主机名部分,请使用SUBSTRING_INDEX()函数:

mysql> SELECT SUBSTRING_INDEX(CURRENT_USER(),'@',1);
+---------------------------------------+
| SUBSTRING_INDEX(CURRENT_USER(),'@',1) |
+---------------------------------------+
| user1                                 |
+---------------------------------------+

mysql> SELECT SUBSTRING_INDEX(CURRENT_USER(),'@',-1);
+----------------------------------------+
| SUBSTRING_INDEX(CURRENT_USER(),'@',-1) |
+----------------------------------------+
| localhost                              |
+----------------------------------------+

8.3 使用加密连接

原文:dev.mysql.com/doc/refman/8.0/en/encrypted-connections.html

8.3.1 配置 MySQL 使用加密连接

8.3.2 加密连接 TLS 协议和密码

8.3.3 创建 SSL 和 RSA 证书和密钥

8.3.4 通过 SSH 从 Windows 远程连接到 MySQL

8.3.5 重用 SSL 会话

在 MySQL 客户端和服务器之间的未加密连接中,可以访问网络的人可以监视所有流量并检查在客户端和服务器之间发送或接收的数据。

当您必须以安全方式在网络上传输信息时,不接受未加密的连接。要使任何类型的数据不可读,请使用加密。加密算法必须包含安全元素,以抵抗许多已知攻击,例如更改加密消息的顺序或重复数据两次。

MySQL 支持使用 TLS(传输层安全性)协议在客户端和服务器之间进行加密连接。TLS 有时被称为 SSL(安全套接字层),但 MySQL 实际上不使用 SSL 协议进行加密连接,因为其加密较弱(参见 第 8.3.2 节,“加密连接 TLS 协议和密码”)。

TLS 使用加密算法确保可以信任通过公共网络接收的数据。它具有检测数据更改、丢失或重放的机制。TLS 还包含使用 X.509 标准进行身份验证的算法。

X.509 使得在互联网上识别某人成为可能。简单来说,应该有一个名为“证书颁发机构”(CA)的实体,为需要的任何人分配电子证书。证书依赖于具有两个加密密钥(公钥和秘密密钥)的非对称加密算法。证书所有者可以向另一方展示证书作为身份证明。证书由其所有者的公钥组成。使用此公钥加密的任何数据只能使用相应的秘密密钥解密,该密钥由证书所有者持有。

MySQL 支持使用 OpenSSL 提供加密连接。有关 OpenSSL 支持的加密协议和密码,请参阅 第 8.3.2 节,“加密连接 TLS 协议和密码”。

默认情况下,MySQL 实例在运行时链接到一个已安装的 OpenSSL 库,以支持加密连接和其他与加密相关的操作。您可以从源代码编译 MySQL,并使用WITH_SSL CMake 选项指定特定已安装的 OpenSSL 版本的路径或替代的 OpenSSL 系统包。在这种情况下,MySQL 会选择该版本。有关如何执行此操作的说明,请参见 Section 2.8.6, “Configuring SSL Library Support”。

从 MySQL 8.0.11 到 8.0.17,可以使用 wolfSSL 编译 MySQL 作为 OpenSSL 的替代方案。从 MySQL 8.0.18 开始,不再支持 wolfSSL,所有 MySQL 构建都使用 OpenSSL。

您可以使用Tls_library_version 系统状态变量在运行时检查 OpenSSL 库的版本,该变量从 MySQL 8.0.30 开始可用。

如果您使用一个版本的 OpenSSL 编译 MySQL,并希望在不重新编译的情况下更改为另一个版本,可以通过编辑动态库加载器路径(Unix 系统上的 LD_LIBRARY_PATH 或 Windows 系统上的 PATH)来实现。删除编译版本的 OpenSSL 路径,并添加替换版本的路径,在路径上任何其他 OpenSSL 库之前放置它。在启动时,当 MySQL 无法在路径上找到使用WITH_SSL 指定的 OpenSSL 版本时,它会使用路径上指定的第一个版本。

默认情况下,MySQL 程序尝试使用加密进行连接,如果服务器支持加密连接,则在无法建立加密连接时回退到未加密连接。有关影响加密连接使用的选项的信息,请参见 Section 8.3.1, “Configuring MySQL to Use Encrypted Connections” 和 Command Options for Encrypted Connections。

MySQL 按连接进行加密,并且对于给定用户的加密使用可以是可选的或强制的。这使您可以根据个别应用程序的要求选择加密或未加密连接。有关如何要求用户使用加密连接的信息,请参见 Section 15.7.1.3, “CREATE USER Statement” 中 CREATE USER 语句的 REQUIRE 子句的讨论。另请参阅 Section 7.1.8, “Server System Variables” 中 require_secure_transport 系统变量的描述。

在源服务器和副本服务器之间可以使用加密连接。参见第 19.3.1 节,“设置复制使用加密连接”。

有关在 MySQL C API 中使用加密连接的信息,请参见支持加密连接。

也可以在 SSH 连接到 MySQL 服务器主机时使用加密连接。例如,请参见第 8.3.4 节,“在 Windows 上通过 SSH 远程连接到 MySQL”。

8.3.1 配置 MySQL 使用加密连接

原文:dev.mysql.com/doc/refman/8.0/en/using-encrypted-connections.html

有几个配置参数可用于指示是否使用加密连接,并指定适当的证书和密钥文件。本节提供了有关为加密连接配置服务器和客户端的一般指导:

  • 用于加密连接的服务器端启动配置

  • 加密连接的服务器端运行时配置和监控

  • 用于加密连接的客户端端配置

  • 将加密连接配置为强制性

还可以在其他情境中使用加密连接,如这些附加部分中所讨论的:

  • 源服务器和副本复制服务器之间。参见 第 19.3.1 节,“设置复制以使用加密连接”。

  • 在组复制服务器之间。参见 第 20.6.2 节,“使用安全套接字层(SSL)保护组通信连接”。

  • 基于 MySQL C API 的客户端程序。参见 支持加密连接。

有关创建所需证书和密钥文件的说明,请参阅 第 8.3.3 节,“创建 SSL 和 RSA 证书和密钥”。

用于加密连接的服务器端启动配置

在服务器端,--ssl 选项指定服务器允许但不要求加密连接。此选项默认启用,因此不需要显式指定。

要求客户端使用加密连接连接,请启用 require_secure_transport 系统变量。参见 将加密连接配置为强制性。

服务器端的这些系统变量指定了服务器在允许客户端建立加密连接时使用的证书和密钥文件:

  • ssl_ca:证书颁发机构(CA)证书文件的路径名。(ssl_capath类似,但指定了 CA 证书文件的目录路径名。)

  • ssl_cert:服务器公钥证书文件的路径名。此证书可以发送给客户端,并根据其拥有的 CA 证书进行验证。

  • ssl_key:服务器私钥文件的路径名。

例如,要启用服务器进行加密连接,请在my.cnf文件中使用以下行启动它,根据需要更改文件名:

[mysqld]
ssl_ca=ca.pem
ssl_cert=server-cert.pem
ssl_key=server-key.pem

要额外指定客户端必须使用加密连接,请启用require_secure_transport系统变量:

[mysqld]
ssl_ca=ca.pem
ssl_cert=server-cert.pem
ssl_key=server-key.pem
require_secure_transport=ON

每个证书和密钥系统变量都指定了一个 PEM 格式的文件。如果需要创建所需的证书和密钥文件,请参阅第 8.3.3 节,“创建 SSL 和 RSA 证书和密钥”。使用 OpenSSL 编译的 MySQL 服务器可以在启动时自动生成缺失的证书和密钥文件。请参阅第 8.3.3.1 节,“使用 MySQL 创建 SSL 和 RSA 证书和密钥”。或者,如果您有 MySQL 源代码分发版,您可以使用其mysql-test/std_data目录中的演示证书和密钥文件测试您的设置。

服务器执行证书和密钥文件的自动发现。如果除了--ssl(可能还包括ssl_cipher)之外没有给出明确的加密连接选项来配置加密连接,服务器会尝试在启动时自动启用加密连接支持:

  • 如果服务器在数据目录中发现名为ca.pemserver-cert.pemserver-key.pem的有效证书和密钥文件,它会启用客户端的加密连接支持。(文件不一定是自动生成的;重要的是它们具有这些名称并且是有效的。)

  • 如果服务器在数据目录中找不到有效的证书和密钥文件,它会继续执行,但不支持加密连接。

如果服务器自动启用了加密连接支持,它会在错误日志中写下一条说明。如果服务器发现 CA 证书是自签名的,它会在错误日志中写下一个警告。(如果证书是由服务器自动创建或使用 mysql_ssl_rsa_setup 手动创建,则证书是自签名的。)

MySQL 还为服务器端提供了这些系统变量来控制加密连接:

  • ssl_cipher:连接加密的可允许密码列表。

  • ssl_crl:包含证书吊销列表的文件的路径名。(ssl_crlpath 类似,但指定了证书吊销列表文件的目录路径名。)

  • tls_versiontls_ciphersuites:服务器允许用于加密连接的加密协议和密码套件;参见 Section 8.3.2, “Encrypted Connection TLS Protocols and Ciphers”。例如,您可以配置 tls_version 来阻止客户端使用不太安全的协议。

如果服务器无法从服务器端加密连接控制的系统变量创建有效的 TLS 上下文,则服务器将在没有加密连接支持的情况下执行。

用于加密连接的服务器端运行时配置和监控

在 MySQL 8.0.16 之前,配置加密连接支持的 tls_*xxx*ssl_*xxx* 系统变量只能在服务器启动时设置。因此,这些系统变量确定服务器为所有新连接使用的 TLS 上下文。

从 MySQL 8.0.16 开始,tls_*xxx*ssl_*xxx* 系统变量是动态的,可以在运行时设置,而不仅仅是在启动时。如果使用 SET GLOBAL 进行更改,新值仅在服务器重新启动之前有效。如果使用 SET PERSIST 进行更改,新值也会延续到后续的服务器重新启动。参见 Section 15.7.6.1, “SET Syntax for Variable Assignment”。然而,对这些变量的运行时更改不会立即影响新连接的 TLS 上下文,后文将对此进行解释。

随着 MySQL 8.0.16 中的更改,使得可以在运行时更改与 TLS 上下文相关的系统变量,服务器还启用了对用于新连接的实际 TLS 上下文的运行时更新。例如,这种功能可能很有用,以避免重新启动运行时间过长以至于 SSL 证书已过期的 MySQL 服务器。

要创建初始 TLS 上下文,服务器使用启动时上下文相关系统变量的值。为了公开上下文值,服务器还初始化了一组相应的状态变量。以下表格显示了定义 TLS 上下文的系统变量以及公开当前活动上下文值的相应状态变量。

表 8.12 服务器主连接接口 TLS 上下文的系统和状态变量

系统变量名称 对应的状态变量名称
ssl_ca Current_tls_ca
ssl_capath Current_tls_capath
ssl_cert Current_tls_cert
ssl_cipher Current_tls_cipher
ssl_crl Current_tls_crl
ssl_crlpath Current_tls_crlpath
ssl_key Current_tls_key
tls_ciphersuites Current_tls_ciphersuites
tls_version Current_tls_version

截至 MySQL 8.0.21,这些活动的 TLS 上下文值也作为性能模式 tls_channel_status 表中的属性公开,以及任何其他活动 TLS 上下文的属性。

要在运行时重新配置 TLS 上下文,请使用以下过程:

  1. 设置每个应更改为新值的与 TLS 上下文相关的系统变量。

  2. 执行ALTER INSTANCE RELOAD TLS。该语句会从 TLS 上下文相关系统变量的当前值重新配置活动的 TLS 上下文。它还会设置相关状态变量以反映新的活动上下文值。该语句需要CONNECTION_ADMIN权限。

  3. 在执行ALTER INSTANCE RELOAD TLS后建立的新连接将使用新的 TLS 上下文。现有连接不受影响。如果需要终止现有连接,请使用KILL语句。

每对系统和状态变量的成员可能由于重新配置过程的方式而暂时具有不同的值:

  • ALTER INSTANCE RELOAD TLS之前对系统变量的更改不会改变 TLS 上下文。在这一点上,这些更改对新连接没有影响,相应的上下文相关系统和状态变量可能具有不同的值。这使您可以对个别系统变量进行任何所需的更改,然后在所有系统变量更改完成后使用ALTER INSTANCE RELOAD TLS原子地更新活动的 TLS 上下文。

  • ALTER INSTANCE RELOAD TLS之后,相应的系统和状态变量具有相同的值。这种情况会一直持续,直到对系统变量进行下一次更改。

在某些情况下,仅通过ALTER INSTANCE RELOAD TLS可能足以重新配置 TLS 上下文,而无需更改任何系统变量。假设由ssl_cert指定的文件中的证书已过期。只需用非过期证书替换现有文件内容,并执行ALTER INSTANCE RELOAD TLS即可使新文件内容被读取并用于新连接。

截至 MySQL 8.0.21,服务器为管理连接接口实现了独立的连接加密配置。请参阅 Administrative Interface Support for Encrypted Connections。此外,ALTER INSTANCE RELOAD TLS 还通过 FOR CHANNEL 子句扩展,允许指定要重新加载 TLS 上下文的通道(接口)。请参阅 Section 15.1.5, “ALTER INSTANCE Statement”。没有状态变量来公开管理接口的 TLS 上下文,但是性能模式 tls_channel_status 表公开了主接口和管理接口的 TLS 属性。请参阅 Section 29.12.21.9, “The tls_channel_status Table”。

更新主接口的 TLS 上下文会产生以下影响:

  • 此更新更改了主连接接口上用于新连接的 TLS 上下文。

  • 此更新还会更改管理接口上用于新连接的 TLS 上下文,除非为该接口配置了某些非默认的 TLS 参数值。

  • 此更新不会影响其他已启用的服务器插件或组件(如 Group Replication 或 X Plugin)使用的 TLS 上下文:

    • 要将主接口重新配置应用于 Group Replication 的组通信连接,这些连接从服务器的与 TLS 上下文相关的系统变量中获取其设置,必须执行 STOP GROUP_REPLICATION,然后执行 START GROUP_REPLICATION 来停止并重新启动 Group Replication。

    • X Plugin 在插件初始化时初始化其 TLS 上下文,如 Section 22.5.3, “Using Encrypted Connections with X Plugin” 中所述。此上下文之后不会更改。

默认情况下,如果配置值不允许创建新的 TLS 上下文,则 RELOAD TLS 操作将出现错误并且不会生效。先前的上下文值将继续用于新连接。如果给定了可选的 NO ROLLBACK ON ERROR 子句并且无法创建新上下文,则不会发生回滚。相反,会生成警告,并且在适用语句的接口上为新连接禁用加密。

在连接接口上启用或禁用加密连接的选项仅在启动时生效。例如,--ssl--admin-ssl选项仅在启动时影响主要和管理接口是否支持加密连接。这些选项在运行时被忽略,对ALTER INSTANCE RELOAD TLS的操作没有影响。例如,您可以使用--ssl=OFF在主接口上禁用加密连接启动服务器,然后重新配置 TLS 并执行ALTER INSTANCE RELOAD TLS在运行时启用加密连接。

客户端端加密连接的配置

有关与建立加密连接相关的客户端选项的完整列表,请参阅加密连接的命令选项。

默认情况下,如果服务器支持加密连接,MySQL 客户端程序会尝试建立加密连接,进一步的控制可以通过--ssl-mode选项进行设置:

  • 在没有--ssl-mode选项的情况下,客户端尝试使用加密连接进行连接,如果无法建立加密连接,则退回到未加密连接。这也是在明确指定--ssl-mode=PREFERRED选项时的行为。

  • 使用--ssl-mode=REQUIRED,客户端需要一个加密连接,如果无法建立加密连接则失败。

  • 使用--ssl-mode=DISABLED,客户端使用一个未加密的连接。

  • 使用--ssl-mode=VERIFY_CA--ssl-mode=VERIFY_IDENTITY,客户端需要一个加密连接,并且还会对服务器 CA 证书进行验证,并且(使用VERIFY_IDENTITY)对其证书中的服务器主机名进行验证。

重要提示

默认设置--ssl-mode=PREFERRED如果其他默认设置未更改,则会产生加密连接。然而,为了防止复杂的中间人攻击,客户端验证服务器的身份非常重要。设置--ssl-mode=VERIFY_CA--ssl-mode=VERIFY_IDENTITY比默认设置更好,以帮助防止这种类型的攻击。VERIFY_CA使客户端检查服务器的证书是否有效。VERIFY_IDENTITY使客户端检查服务器的证书是否有效,并且使客户端检查客户端使用的主机名是否与服务器证书中的身份匹配。要实施这些设置之一,您必须首先确保服务器的 CA 证书可靠地对所有在您的环境中使用它的客户端可用,否则将导致可用性问题。因此,它们不是默认设置。

如果在服务器端启用了require_secure_transport系统变量以导致服务器要求加密连接,则尝试建立未加密连接将失败。请参阅将加密连接配置为强制性。

客户端端的以下选项标识客户端在与服务器建立加密连接时使用的证书和密钥文件。它们类似于服务器端使用的ssl_cassl_certssl_key系统变量,但--ssl-cert--ssl-key标识客户端的公钥和私钥:

  • --ssl-ca:证书颁发机构(CA)证书文件的路径名。如果使用此选项,必须指定与服务器使用的相同证书。(--ssl-capath类似,但指定 CA 证书文件目录的路径名。)

  • --ssl-cert:客户端公钥证书文件的路径名。

  • --ssl-key:客户端私钥文件的路径名。

为了相对于默认加密提供额外的安全性,客户端可以提供与服务器使用的 CA 证书匹配的证书,并启用主机名身份验证。这样,服务器和客户端都信任同一 CA 证书,并且客户端验证连接的主机是预期的主机:

  • 要指定 CA 证书,请使用--ssl-ca(或--ssl-capath),并指定--ssl-mode=VERIFY_CA

  • 若要启用主机名身份验证,请使用--ssl-mode=VERIFY_IDENTITY而不是--ssl-mode=VERIFY_CA

注意

使用VERIFY_IDENTITY进行主机名身份验证无法与服务器自动创建的自签名证书或使用mysql_ssl_rsa_setup手动创建的证书一起使用(参见第 8.3.3.1 节,“使用 MySQL 创建 SSL 和 RSA 证书和密钥”)。这些自签名证书不包含服务器名称作为通用名称值。

在 MySQL 8.0.12 之前,主机名身份验证也无法与使用通配符指定通用名称的证书一起使用,因为该名称与服务器名称逐字比较。

MySQL 还为客户端提供了这些选项来控制客户端端的加密连接:

  • --ssl-cipher: 连接加密的允许密码列表。

  • --ssl-crl: 包含证书吊销列表的文件路径名。(--ssl-crlpath类似,但指定证书吊销列表文件目录的路径名。)

  • --tls-version, --tls-ciphersuites: 允许的加密协议和密码套件;参见第 8.3.2 节,“加密连接 TLS 协议和密码”。

根据客户端使用的 MySQL 帐户的加密要求,客户端可能需要指定某些选项以使用加密连接到 MySQL 服务器。

假设您想使用没有特殊加密要求的帐户连接,或者使用包含REQUIRE SSL子句的CREATE USER语句创建的帐户。假设服务器支持加密连接,客户端可以使用没有--ssl-mode选项或明确的--ssl-mode=PREFERRED选项连接使用加密:

mysql

或者:

mysql --ssl-mode=PREFERRED

对于使用REQUIRE SSL子句创建的帐户,如果无法建立加密连接,则连接尝试将失败。对于没有特殊加密要求的帐户,如果无法建立加密连接,则尝试回退到未加密连接。为了防止回退并在无法获取加密连接时失败,请这样连接:

mysql --ssl-mode=REQUIRED

如果帐户具有更严格的安全要求,则必须指定其他选项以建立加密连接:

  • 对于使用REQUIRE X509子句创建的帐户,客户端必须至少指定--ssl-cert--ssl-key。此外,建议使用--ssl-ca(或--ssl-capath)以便验证服务器提供的公共证书。例如(在一行上输入命令):

    mysql --ssl-ca=ca.pem
          --ssl-cert=client-cert.pem
          --ssl-key=client-key.pem
    
  • 对于使用REQUIRE ISSUERREQUIRE SUBJECT子句创建的帐户,加密要求与REQUIRE X509相同,但证书必须分别与帐户定义中指定的发行者或主题匹配。

有关REQUIRE子句的更多信息,请参见第 15.7.1.3 节,“CREATE USER Statement”。

MySQL 服务器可以生成客户端证书和密钥文件,客户端可以使用这些文件连接到 MySQL 服务器实例。请参见第 8.3.3 节,“创建 SSL 和 RSA 证书和密钥”。

重要提示

如果连接到 MySQL 服务器实例的客户端使用具有extendedKeyUsage扩展(X.509 v3 扩展)的 SSL 证书,则扩展密钥用途必须包括客户端身份验证(clientAuth)。如果 SSL 证书仅用于服务器身份验证(serverAuth)和其他非客户端证书目的,证书验证将失败,客户端连接到 MySQL 服务器实例将失败。MySQL Server 生成的 SSL 证书中没有extendedKeyUsage扩展(如第 8.3.3.1 节,“使用 MySQL 创建 SSL 和 RSA 证书和密钥”中所述),并且使用openssl命令创建的 SSL 证书遵循第 8.3.3.2 节,“使用 openssl 创建 SSL 证书和密钥”中的说明。如果使用其他方式创建的客户端证书,请确保任何extendedKeyUsage扩展包括客户端身份验证。

为了防止使用加密并覆盖其他--ssl-*xxx*选项,请使用--ssl-mode=DISABLED调用客户端程序:

mysql --ssl-mode=DISABLED

要确定当前与服务器的连接是否使用加密,请检查 Ssl_cipher 状态变量的会话值。如果值为空,则连接未加密。否则,连接已加密,并且该值指示加密密码。例如:

mysql> SHOW SESSION STATUS LIKE 'Ssl_cipher';
+---------------+---------------------------+
| Variable_name | Value                     |
+---------------+---------------------------+
| Ssl_cipher    | DHE-RSA-AES128-GCM-SHA256 |
+---------------+---------------------------+

对于 mysql 客户端,另一种方法是使用 STATUS\s 命令并检查 SSL 行:

mysql> \s
...
SSL: Not in use
...

或者:

mysql> \s
...
SSL: Cipher in use is DHE-RSA-AES128-GCM-SHA256
...

配置加密连接为强制性

对于一些 MySQL 部署,使用加密连接可能不仅仅是可取的,而是强制性的(例如,为了满足监管要求)。本节讨论了使您能够执行此操作的配置设置。这些控制级别可用:

  • 您可以配置服务器要求客户端使用加密连接进行连接。

  • 您可以调用单独的客户端程序要求加密连接,即使服务器允许但不要求加密。

  • 您可以配置单独的 MySQL 帐户仅在加密连接上可用。

要求客户端使用加密连接连接,请启用 require_secure_transport 系统变量。例如,在服务器的 my.cnf 文件中添加以下行:

[mysqld]
require_secure_transport=ON

或者,要在运行时设置并持久化该值,请使用以下语句:

SET PERSIST require_secure_transport=ON;

SET PERSIST 为运行中的 MySQL 实例设置一个值。它还保存该值,导致其在后续服务器重启时使用。参见 Section 15.7.6.1, “SET Syntax for Variable Assignment”。

启用 require_secure_transport 后,客户端连接到服务器需要使用某种形式的安全传输,服务器仅允许使用 SSL 的 TCP/IP 连接,或者使用套接字文件(在 Unix 上)或共享内存(在 Windows 上)的连接。服务器拒绝非安全连接尝试,这些尝试将因 ER_SECURE_TRANSPORT_REQUIRED 错误而失败。

要调用客户端程序,使其要求加密连接,无论服务器是否要求加密,请使用 --ssl-mode 选项值 REQUIREDVERIFY_CAVERIFY_IDENTITY。例如:

mysql --ssl-mode=REQUIRED
mysqldump --ssl-mode=VERIFY_CA
mysqladmin --ssl-mode=VERIFY_IDENTITY

要配置一个 MySQL 帐户仅在加密连接上可用,请在创建帐户的 CREATE USER 语句中包含一个 REQUIRE 子句,指定您需要的加密特性。例如,要求加密连接和使用有效的 X.509 证书,使用 REQUIRE X509

CREATE USER 'jeffrey'@'localhost' REQUIRE X509;

有关REQUIRE子句的更多信息,请参见第 15.7.1.3 节,“CREATE USER Statement”。

要修改没有加密要求的现有帐户,请使用ALTER USER语句。

8.3.2 加密连接 TLS 协议和密码

原文:dev.mysql.com/doc/refman/8.0/en/encrypted-connection-protocols-ciphers.html

MySQL 支持多个 TLS 协议和密码,并允许配置哪些协议和密码用于加密连接。还可以确定当前会话使用的协议和密码。

  • 支持的 TLS 协议

  • 移除对 TLSv1 和 TLSv1.1 协议的支持

  • 连接 TLS 协议配置

  • 连接密码配置

  • 连接 TLS 协议协商

  • 监视当前客户端会话的 TLS 协议和密码

支持的 TLS 协议

允许连接到给定 MySQL 服务器实例的协议集受多个因素影响,如下所示:

MySQL 服务器版本

  • 直到 MySQL 8.0.15,MySQL 支持 TLSv1、TLSv1.1 和 TLSv1.2 协议。

  • 从 MySQL 8.0.16 开始,MySQL 还支持 TLSv1.3 协议。要使用 TLSv1.3,MySQL 服务器和客户端应用程序必须使用 OpenSSL 1.1.1 或更高版本进行编译。从 MySQL 8.0.18 开始,Group Replication 组件支持 TLSv1.3(有关详细信息,请参见第 20.6.2 节,“使用安全套接字层(SSL)保护组通信连接”)。

  • 截至 MySQL 8.0.26 版本,TLSv1 和 TLSv1.1 协议已被弃用。这些协议版本较旧,分别于 1996 年和 2006 年发布,使用的算法较弱且过时。有关背景,请参考 IETF 备忘录Deprecating TLSv1.0 and TLSv1.1

  • 截至 MySQL 8.0.28 版本,MySQL 不再支持 TLSv1 和 TLSv1.1 协议。从此版本开始,客户端不能使用协议设置为 TLSv1 或 TLSv1.1 进行 TLS/SSL 连接。有关更多详细信息,请参见移除对 TLSv1 和 TLSv1.1 协议的支持。

表 8.13 MySQL 服务器 TLS 协议支持

MySQL 服务器版本 支持的 TLS 协议
MySQL 8.0.15 及以下版本 TLSv1、TLSv1.1、TLSv1.2
MySQL 8.0.16 和 MySQL 8.0.17 TLSv1、TLSv1.1、TLSv1.2、TLSv1.3(不包括 Group Replication)
MySQL 8.0.18 到 MySQL 8.0.25 TLSv1、TLSv1.1、TLSv1.2、TLSv1.3(包括 Group Replication)
MySQL 8.0.26 和 MySQL 8.0.27 TLSv1(已弃用)、TLSv1.1(已弃用)、TLSv1.2、TLSv1.3
MySQL 8.0.28 及更高版本 TLSv1.2、TLSv1.3

SSL 库

如果 SSL 库不支持特定协议,MySQL 也不支持,以下讨论中指定该协议的任何部分都不适用。特别要注意的是,要使用 TLSv1.3,MySQL 服务器和客户端应用程序都必须使用 OpenSSL 1.1.1 或更高版本进行编译。MySQL 服务器在启动时检查 OpenSSL 的版本,如果低于 1.1.1,则会从与 TLS 版本相关的服务器系统变量的默认值中删除 TLSv1.3(tls_versionadmin_tls_versiongroup_replication_recovery_tls_version)。

MySQL 实例配置

可以在服务器端和客户端上配置允许的 TLS 协议,只包括受支持的 TLS 协议的子集。双方的配置必须至少包括一个共同的协议,否则连接尝试无法协商要使用的协议。有关详细信息,请参阅 连接 TLS 协议协商。

系统范围的主机配置

主机系统可能只允许某些 TLS 协议,这意味着即使 MySQL 本身允许它们,MySQL 连接也不能使用非允许的协议:

  • 假设 MySQL 配置允许 TLSv1、TLSv1.1 和 TLSv1.2,但您的主机系统配置只允许使用 TLSv1.2 或更高版本的连接。在这种情况下,即使 MySQL 配置允许它们,您也无法建立使用 TLSv1 或 TLSv1.1 的 MySQL 连接,因为主机系统不允许���们。

  • 如果 MySQL 配置允许 TLSv1、TLSv1.1 和 TLSv1.2,但您的主机系统配置只允许使用 TLSv1.3 或更高版本的连接,则根本无法建立 MySQL 连接,因为 MySQL 允许的任何协议都不被主机系统允许。

解决此问题的方法包括:

  • 更改系统范围的主机配置以允许额外的 TLS 协议。请查阅操作系统文档以获取说明。例如,您的系统可能有一个包含以下行的 /etc/ssl/openssl.cnf 文件,以将 TLS 协议限制为 TLSv1.2 或更高版本:

    [system_default_sect]
    MinProtocol = TLSv1.2
    

    将值更改为较低的协议版本或None会使系统更加宽松。这种解决方法的缺点是允许较低(不太安全)的协议可能会产生不利的安全后果。

  • 如果您无法或不愿更改主机系统的 TLS 配置,请将 MySQL 应用程序更改为使用主机系统允许的更高(更安全)的 TLS 协议。对于仅支持较低协议版本的旧版本 MySQL 可能无法实现此目标。例如,在 MySQL 5.6.46 之前,只支持 TLSv1 协议,因此即使客户端来自支持更高协议版本的新 MySQL 版本,尝试连接到早于 5.6.46 的服务器也会失败。在这种情况下,可能需要升级到支持额外 TLS 版本的 MySQL 版本。

不再支持 TLSv1 和 TLSv1.1 协议。

从 MySQL 8.0.28 开始,不再支持 TLSv1 和 TLSv1.1 连接协议。这些协议从 MySQL 8.0.26 开始被弃用。有关背景,请参考 IETF 备忘录Deprecating TLSv1.0 and TLSv1.1。建议使用更安全的 TLSv1.2 和 TLSv1.3 协议进行连接。TLSv1.3 要求 MySQL 服务器和客户端应用程序都使用 OpenSSL 1.1.1 进行编译。

不再支持 TLSv1 和 TLSv1.1 是因为这些协议版本过时,分别于 1996 年和 2006 年发布。使用的算法过于薄弱和过时。除非您使用非常旧的 MySQL Server 或连接器版本,否则不太可能使用 TLSv1.0 或 TLSv1.1 进行连接。MySQL 连接器和客户端默认选择可用的最高 TLS 版本。

在不支持 TLSv1 和 TLSv1.1 连接协议的版本中(从 MySQL 8.0.28 开始),包括 MySQL Shell 在内支持用于指定连接到 MySQL 服务器的 TLS 协议的--tls-version选项的客户端无法使用设置为 TLSv1 或 TLSv1.1 的协议进行 TLS/SSL 连接。如果客户端尝试使用这些协议进行连接,则对于 TCP 连接,连接将失败,并向客户端返回错误。对于套接字连接,如果--ssl-mode设置为REQUIRED,则连接将失败,否则将进行连接,但禁用 TLS/SSL。

从 MySQL 8.0.28 开始,服务器端的以下设置已更改:

  • 服务器的tls_versionadmin_tls_version系统变量的默认值不再包括 TLSv1 和 TLSv1.1。

  • Group Replication 系统变量group_replication_recovery_tls_version的默认值不再包括 TLSv1 和 TLSv1.1。

  • 对于异步复制,副本不能将与源服务器的连接协议设置为 TLSv1 或 TLSv1.1(CHANGE REPLICATION SOURCE TO 语句的 SOURCE_TLS_VERSION 选项)。

在 TLSv1 和 TLSv1.1 连接协议被弃用的版本中(MySQL 8.0.26 和 MySQL 8.0.27),如果它们包含在 tls_versionadmin_tls_version 系统变量的值中,并且客户端成功使用它们连接,则服务器会在错误日志中写入警告。如果在运行时设置弃用的协议并使用 ALTER INSTANCE RELOAD TLS 语句实现它们,也会返回警告。如果配置允许弃用的 TLS 协议,包括为与源服务器的连接指定 TLS 协议的副本和为分布式恢复连接指定 TLS 协议的 Group Replication 组成员,那么客户端不会发出警告。

更多信息,请参见 MySQL 8.0 是否支持 TLS 1.0 和 1.1?

连接 TLS 协议配置

在服务器端,tls_version 系统变量的值确定 MySQL 服务器允许加密连接使用的 TLS 协议。tls_version 值适用于来自客户端的连接、常规源/副本复制连接(其中此服务器实例是源)、Group Replication 组通信连接以及 Group Replication 分布式恢复连接(其中此服务器实例是捐赠者)。管理连接接口配置类似,但使用 admin_tls_version 系统变量(参见第 7.1.12.2 节,“管理连接管理”)。此讨论也适用于 admin_tls_version

tls_version 值是一个或多个逗号分隔的 TLS 协议版本列表,不区分大小写。默认情况下,此变量列出了编译 MySQL 和 MySQL 服务器发布所使用的 SSL 库支持的所有协议。因此,默认设置如表 8.14,“MySQL 服务器 TLS 协议默认设置”所示。

表 8.14 MySQL 服务器 TLS 协议默认设置

MySQL 服务器发布版本 tls_version 默认设置
MySQL 8.0.15 及以下版本 TLSv1,TLSv1.1,TLSv1.2
MySQL 8.0.16 和 MySQL 8.0.17 TLSv1,TLSv1.1,TLSv1.2,TLSv1.3 (使用 OpenSSL 1.1.1) TLSv1,TLSv1.1,TLSv1.2 (其他情况) Group Replication 不支持 TLSv1.3
MySQL 8.0.18 到 MySQL 8.0.25 TLSv1,TLSv1.1,TLSv1.2,TLSv1.3 (使用 OpenSSL 1.1.1) TLSv1,TLSv1.1,TLSv1.2 (其他情况) Group Replication 支持 TLSv1.3
MySQL 8.0.26 和 MySQL 8.0.27 TLSv1,TLSv1.1,TLSv1.2,TLSv1.3 (使用 OpenSSL 1.1.1) TLSv1,TLSv1.1,TLSv1.2 (其他情况) TLSv1 和 TLSv1.1 已被弃用
MySQL 8.0.28 及以上版本 TLSv1.2,TLSv1.3

要在运行时确定 tls_version 的值,请使用以下语句:

mysql> SHOW GLOBAL VARIABLES LIKE 'tls_version';
+---------------+-----------------------+
| Variable_name | Value                 |
+---------------+-----------------------+
| tls_version   | TLSv1.2,TLSv1.3       |
+---------------+-----------------------+

要更改 tls_version 的值,请在服务器启动时设置。例如,要允许使用 TLSv1.2 或 TLSv1.3 协议的连接,但禁止使用较不安全的 TLSv1 和 TLSv1.1 协议的连接,请在服务器的 my.cnf 文件中使用以下行:

[mysqld]
tls_version=TLSv1.2,TLSv1.3

要更加严格并仅允许 TLSv1.3 连接,设置 tls_version 如下:

[mysqld]
tls_version=TLSv1.3

从 MySQL 8.0.16 开始,tls_version 可以在运行时更改。请参阅 Server-Side Runtime Configuration and Monitoring for Encrypted Connections。

在客户端,--tls-version 选项指定客户端程序允许连接到服务器的 TLS 协议版本。选项值的格式与之前描述的 tls_version 系统变量相同(一个或多个逗号分隔的协议版本列表)。

对于源/复制复制连接,其中此服务器实例是复制品,CHANGE REPLICATION SOURCE TO 语句(从 MySQL 8.0.23 开始)或 CHANGE MASTER TO 语句(MySQL 8.0.23 之前)的 SOURCE_TLS_VERSION | MASTER_TLS_VERSION 选项指定复制品允许连接到源的 TLS 协议版本。选项值的格式与之前描述的 tls_version 系统变量相同。参见 Section 19.3.1, “Setting Up Replication to Use Encrypted Connections”。

可以为SOURCE_TLS_VERSION | MASTER_TLS_VERSION指定的协议取决于 SSL 库。此选项独立于服务器tls_version值,并不受其影响。例如,作为副本的服务器可以配置tls_version设置为 TLSv1.3,以仅允许使用 TLSv1.3 的传入连接,但也可以配置SOURCE_TLS_VERSION | MASTER_TLS_VERSION设置为 TLSv1.2,以仅允许对源的传出副本连接使用 TLSv1.2。

对于 Group Replication 分布式恢复连接,其中此服务器实例是发起分布式恢复的加入成员(即客户端),group_replication_recovery_tls_version系统变量指定客户端允许的协议。同样,此选项独立于服务器tls_version值,并不受其影响,当此服务器实例是捐赠者时应用。Group Replication 服务器通常在其组成员身份的过程中既作为捐赠者又作为加入成员参与分布式恢复,因此应设置这两个系统变量。参见第 20.6.2 节,“使用安全套接字层(SSL)保护组通信连接”。

TLS 协议配置影响给定连接使用的协议,如连接 TLS 协议协商中所述。

允许的协议应该被选择,以免在列表中留下“漏洞”。例如,这些服务器配置数值没有漏洞:

tls_version=TLSv1,TLSv1.1,TLSv1.2,TLSv1.3
tls_version=TLSv1.1,TLSv1.2,TLSv1.3
tls_version=TLSv1.2,TLSv1.3
tls_version=TLSv1.3

这些数值有漏洞,不应使用:

tls_version=TLSv1,TLSv1.2       *(TLSv1.1 is missing)* tls_version=TLSv1.1,TLSv1.3     *(TLSv1.2 is missing)*

对于其他配置上下文,如客户端或副本,也适用于禁止漏洞。

除非你打算禁用加密连接,否则允许的协议列表不应为空。如果将 TLS 版本参数设置为空字符串,加密连接将无法建立:

  • tls_version:服务器不允许加密传入连接。

  • --tls-version:客户端不允许向服务器建立加密传出连接。

  • SOURCE_TLS_VERSION | MASTER_TLS_VERSION:副本不允许向源建立加密传出连接。

  • group_replication_recovery_tls_version:加入成员不允许与分布式恢复连接建立加密连接。

连接密码配置

一组默认的密码适用于加密连接,可以通过显式配置允许的密码来覆盖。在连接建立过程中,连接的双方必须允许一些共同的密码,否则连接将失败。在双��都允许的密码中,SSL 库选择由提供的具有最高优先级的证书支持的密码。

要指定适用于使用 TLS 协议直至 TLSv1.2 的加密连接的密码或密码:

  • 在服务器端设置ssl_cipher系统变量,并为客户端程序使用--ssl-cipher选项。

  • 对于常规的源/副本复制连接,在这个服务器实例作为源时,设置ssl_cipher系统变量。当这个服务器实例作为副本时,使用CHANGE REPLICATION SOURCE TO语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO语句(MySQL 8.0.23 之前)的SOURCE_SSL_CIPHER | MASTER_SSL_CIPHER选项。参见 Section 19.3.1, “Setting Up Replication to Use Encrypted Connections”。

  • 对于 Group Replication 组成员,对于 Group Replication 组通信连接以及 Group Replication 分布式恢复连接(其中这个服务器实例是捐赠者),设置ssl_cipher系统变量。对于 Group Replication 分布式恢复连接(其中这个服务器实例是加入成员),使用group_replication_recovery_ssl_cipher系统变量。参见 Section 20.6.2, “Securing Group Communication Connections with Secure Socket Layer (SSL)”")。

对于使用 TLSv1.3 的加密连接,OpenSSL 1.1.1 及更高版本支持以下密码套件,默认情况下启用前三个:

TLS_AES_128_GCM_SHA256
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_CCM_SHA256

注意

在 MySQL 8.0.35 之前,TLS_AES_128_CCM_8_SHA256支持与服务器系统变量--tls-ciphersuites--admin-tls-ciphersuites一起使用。如果为 MySQL 8.0.35 及更高版本配置TLS_AES_128_CCM_8_SHA256,将生成一个弃用警告。

要显式配置允许的 TLSv1.3 密码套件,请设置以下参数。在每种情况下,配置值是一个零个或多个以冒号分隔的密码套件名称列表。

  • 在服务器端,使用tls_ciphersuites系统变量。如果未设置此变量,则其默认值为NULL,这意味着服务器允许默认一组密码套件。如果将变量设置为空字符串,则不启用任何密码套件,无法建立加密连接。

  • 在客户端端,使用--tls-ciphersuites选项。如果未设置此选项,则客户端允许默认一组密码套件。如果将选项设置为空字符串,则不启用任何密码套件,无法建立加密连接。

  • 对于常规的源/副本复制连接,在此服务器实例作为源时,请使用tls_ciphersuites系统变量。在此服务器实例作为副本时,请使用SOURCE_TLS_CIPHERSUITES | MASTER_TLS_CIPHERSUITES选项,用于CHANGE REPLICATION SOURCE TO语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO语句(在 MySQL 8.0.23 之前)。参见 Section 19.3.1, “Setting Up Replication to Use Encrypted Connections”。

  • 对于 Group Replication 组成员,在 Group Replication 组通信连接以及在此服务器实例作为捐赠者时的 Group Replication 分布式恢复连接中,请使用tls_ciphersuites系统变量。对于此服务器实例作为加入成员时的 Group Replication 分布式恢复连接,请使用group_replication_recovery_tls_ciphersuites系统变量。参见 Section 20.6.2, “Securing Group Communication Connections with Secure Socket Layer (SSL)”")。

注意

MySQL 8.0.16 版本开始支持密码套件,但要求 MySQL 服务器和客户端应用程序都使用 OpenSSL 1.1.1 或更高版本进行编译。

在 MySQL 8.0.16 至 8.0.18 中,group_replication_recovery_tls_ciphersuites系统变量和CHANGE REPLICATION SOURCE TO语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO语句(在 MySQL 8.0.23 之前)中的SOURCE_TLS_CIPHERSUITES | MASTER_TLS_CIPHERSUITES选项不可用。在这些版本中,如果用于源/副本复制连接的 TLSv1.3,或在分布式恢复的 Group Replication 中使用 TLSv1.3(从 MySQL 8.0.18 开始支持),则复制源或 Group Replication 捐赠服务器必须允许至少启用默认情况下启用的一个 TLSv1.3 密码套件。从 MySQL 8.0.19 开始,您可以使用选项配置客户端支持任何选择的密码套件,包括仅使用非默认密码套件(如果需要)。

给定密码可能仅适用于特定的 TLS 协议,这会影响 TLS 协议协商过程。请参阅连接 TLS 协议协商。

要确定给定服务器支持哪些密码,检查Ssl_cipher_list状态变量的会话值:

SHOW SESSION STATUS LIKE 'Ssl_cipher_list';

Ssl_cipher_list状态变量列出了可能的 SSL 密码(非 SSL 连接为空)。如果 MySQL 支持 TLSv1.3,则该值包括可能的 TLSv1.3 密码套件。

注意

ECDSA 密码只能与使用 ECDSA 进行数字签名的 SSL 证书结合使用,并且不能与使用 RSA 的证书一起使用。MySQL 服务器的 SSL 证书自动生成过程不生成 ECDSA 签名证书,它只生成 RSA 签名证书。除非您有可用的 ECDSA 证书,请勿选择 ECDSA 密码。

对于使用 TLS.v1.3 的加密连接,MySQL 使用 SSL 库的默认密码套件列表。

对于使用 TLS 协议直至 TLSv1.2 的加密连接,MySQL 将以下默认密码列表传递给 SSL 库。

ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-RSA-CHACHA20-POLY1305
ECDHE-ECDSA-AES256-CCM
ECDHE-ECDSA-AES128-CCM
DHE-RSA-AES128-GCM-SHA256
DHE-RSA-AES256-GCM-SHA384
DHE-RSA-AES256-CCM
DHE-RSA-AES128-CCM
DHE-RSA-CHACHA20-POLY1305

这些密码限制已经生效:

  • 从 MySQL 8.0.35 开始,以下密码已被弃用,并在与服务器系统变量--ssl-cipher--admin-ssl-cipher一起使用时产生警告:

    ECDHE-ECDSA-AES128-SHA256
    ECDHE-RSA-AES128-SHA256
    ECDHE-ECDSA-AES256-SHA384
    ECDHE-RSA-AES256-SHA384
    DHE-DSS-AES128-GCM-SHA256
    DHE-RSA-AES128-SHA256
    DHE-DSS-AES128-SHA256
    DHE-DSS-AES256-GCM-SHA384
    DHE-RSA-AES256-SHA256
    DHE-DSS-AES256-SHA256
    ECDHE-RSA-AES128-SHA
    ECDHE-ECDSA-AES128-SHA
    ECDHE-RSA-AES256-SHA
    ECDHE-ECDSA-AES256-SHA
    DHE-DSS-AES128-SHA
    DHE-RSA-AES128-SHA
    TLS_DHE_DSS_WITH_AES_256_CBC_SHA
    DHE-RSA-AES256-SHA
    AES128-GCM-SHA256
    DH-DSS-AES128-GCM-SHA256
    ECDH-ECDSA-AES128-GCM-SHA256
    AES256-GCM-SHA384
    DH-DSS-AES256-GCM-SHA384
    ECDH-ECDSA-AES256-GCM-SHA384
    AES128-SHA256
    DH-DSS-AES128-SHA256
    ECDH-ECDSA-AES128-SHA256
    AES256-SHA256
    DH-DSS-AES256-SHA256
    ECDH-ECDSA-AES256-SHA384
    AES128-SHA
    DH-DSS-AES128-SHA
    ECDH-ECDSA-AES128-SHA
    AES256-SHA
    DH-DSS-AES256-SHA
    ECDH-ECDSA-AES256-SHA
    DH-RSA-AES128-GCM-SHA256
    ECDH-RSA-AES128-GCM-SHA256
    DH-RSA-AES256-GCM-SHA384
    ECDH-RSA-AES256-GCM-SHA384
    DH-RSA-AES128-SHA256
    ECDH-RSA-AES128-SHA256
    DH-RSA-AES256-SHA256
    ECDH-RSA-AES256-SHA384
    ECDHE-RSA-AES128-SHA
    ECDHE-ECDSA-AES128-SHA
    ECDHE-RSA-AES256-SHA
    ECDHE-ECDSA-AES256-SHA
    DHE-DSS-AES128-SHA
    DHE-RSA-AES128-SHA
    TLS_DHE_DSS_WITH_AES_256_CBC_SHA
    DHE-RSA-AES256-SHA
    AES128-SHA
    DH-DSS-AES128-SHA
    ECDH-ECDSA-AES128-SHA
    AES256-SHA
    DH-DSS-AES256-SHA
    ECDH-ECDSA-AES256-SHA
    DH-RSA-AES128-SHA
    ECDH-RSA-AES128-SHA
    DH-RSA-AES256-SHA
    ECDH-RSA-AES256-SHA
    DES-CBC3-SHA
    
  • 以下密码已被永久限制:

    !DHE-DSS-DES-CBC3-SHA
    !DHE-RSA-DES-CBC3-SHA
    !ECDH-RSA-DES-CBC3-SHA
    !ECDH-ECDSA-DES-CBC3-SHA
    !ECDHE-RSA-DES-CBC3-SHA
    !ECDHE-ECDSA-DES-CBC3-SHA
    
  • 以下密码类别已被永久限制:

    !aNULL
    !eNULL
    !EXPORT
    !LOW
    !MD5
    !DES
    !RC2
    !RC4
    !PSK
    !SSLv3
    

如果服务器启动时使用ssl_cert系统变量设置为使用任何前述受限密码或密码类别的证书,则服务器将以禁用加密连接的支持启动。

连接 TLS 协议协商

在 MySQL 中,连接尝试会协商双方都支持的最高 TLS 协议版本,该版本在双方都支持的协议兼容加密密码上可用。协商过程取决于诸如用于编译服务器和客户端的 SSL 库、TLS 协议和加密密码配置以及使用的密钥大小等因素:

  • 要使连接尝试成功,服务器和客户端 TLS 协议配置必须允许一些共同的协议。

  • 类似地,服务器和客户端加密密码配置必须允许一些共同的密码。给定的密码可能仅适用于特定的 TLS 协议,因此除非还有兼容的密码,否则在协商过程中不会选择协议。

  • 如果 TLSv1.3 可用,则尽可能使用。 (这意味着服务器和客户端配置都必须允许 TLSv1.3,并且两者还必须允许一些 TLSv1.3 兼容的加密密码。)否则,MySQL 将继续使用可用协议列表,如果可能的话使用 TLSv1.2,依此类推。协商从更安全的协议到不太安全的协议进行。协商顺序与配置协议的顺序无关。例如,无论tls_version的值是TLSv1,TLSv1.1,TLSv1.2,TLSv1.3还是TLSv1.3,TLSv1.2,TLSv1.1,TLSv1,协商顺序都是相同的。

  • TLSv1.2 与所有密钥大小为 512 位或更小的密码不兼容。要使用此协议与此类密钥,请在服务器端设置ssl_cipher系统变量,或使用--ssl-cipher客户端选项显式指定密码名称:

    AES128-SHA
    AES128-SHA256
    AES256-SHA
    AES256-SHA256
    CAMELLIA128-SHA
    CAMELLIA256-SHA
    DES-CBC3-SHA
    DHE-RSA-AES256-SHA
    RC4-MD5
    RC4-SHA
    SEED-SHA
    
  • 为了更好的安全性,请使用至少 2048 位的 RSA 密钥大小的证书。

如果服务器和客户端没有共同的允许协议,以及共同的协议兼容密码,服务器将终止连接请求。例如:

  • 如果服务器配置为tls_version=TLSv1.1,TLSv1.2

    • 对于使用--tls-version=TLSv1调用的客户端以及仅支持 TLSv1 的旧客户端,连接尝试将失败。

    • 类似地,为配置为MASTER_TLS_VERSION = 'TLSv1'的复制品以及仅支持 TLSv1 的旧复制品,连接尝试将失败。

  • 如果服务器配置为tls_version=TLSv1或是仅支持 TLSv1 的旧服务器:

    • 对于使用--tls-version=TLSv1.1,TLSv1.2调用的客户端,连接尝试将失败。

    • 同样,对于配置为MASTER_TLS_VERSION = 'TLSv1.1,TLSv1.2'的副本,连接尝试将失败。

MySQL 允许指定要支持的协议列表。此列表直接传递给底层 SSL 库,最终由该库决定实际启用来自提供的列表的哪些协议。有关 SSL 库处理此操作的信息,请参考 MySQL 源代码和 OpenSSL SSL_CTX_new() 文档。

监控当前客户端会话的 TLS 协议和密码

要确定当前客户端会话使用的加密 TLS 协议和密码,检查Ssl_versionSsl_cipher状态变量的会话值:

mysql> SELECT * FROM performance_schema.session_status
       WHERE VARIABLE_NAME IN ('Ssl_version','Ssl_cipher');
+---------------+---------------------------+
| VARIABLE_NAME | VARIABLE_VALUE            |
+---------------+---------------------------+
| Ssl_cipher    | DHE-RSA-AES128-GCM-SHA256 |
| Ssl_version   | TLSv1.2                   |
+---------------+---------------------------+

如果连接未加密,则两个变量的值为空。

8.3.3 创建 SSL 和 RSA 证书和密钥

原文:dev.mysql.com/doc/refman/8.0/en/creating-ssl-rsa-files.html

8.3.3.1 使用 MySQL 创建 SSL 和 RSA 证书和密钥

8.3.3.2 使用 openssl 创建 SSL 证书和密钥

8.3.3.3 使用 openssl 创建 RSA 密钥

以下讨论描述了如何在 MySQL 中创建 SSL 和 RSA 支持所需的文件。文件的创建可以使用 MySQL 自身提供的工具,也可以直接调用 openssl 命令来执行。

SSL 证书和密钥文件使 MySQL 能够支持使用 SSL 进行加密连接。参见 第 8.3.1 节,“配置 MySQL 使用加密连接”。

RSA 密钥文件使 MySQL 能够支持通过 sha256_passwordcaching_sha2_password 插件对由未加密连接进行身份验证的帐户进行安全密码交换。参见 第 8.4.1.3 节,“SHA-256 可插拔认证”,以及 第 8.4.1.2 节,“缓存 SHA-2 可插拔认证”。

原文:dev.mysql.com/doc/refman/8.0/en/creating-ssl-rsa-files-using-mysql.html

8.3.3.1 使用 MySQL 创建 SSL 和 RSA 证书和密钥

MySQL 提供了以下方法来创建 SSL 证书和密钥文件以及 RSA 密钥对文件,以支持使用 SSL 进行加密连接和在未加密连接上使用 RSA 进行安全密码交换,如果这些文件丢失:

  • 服务器可以在启动时自动生成这些文件,适用于 MySQL 发行版。

  • 用户可以手动调用mysql_ssl_rsa_setup实用程序(自 MySQL 8.0.34 起已弃用)。

  • 对于某些发行类型,如 RPM 和 DEB 软件包,mysql_ssl_rsa_setup在数据目录初始化期间调用。在这种情况下,只要openssl命令可用,MySQL 发行版不必使用 OpenSSL 进行编译。

重要

服务器自动生成和mysql_ssl_rsa_setup有助于降低使用 SSL 的门槛,使生成所需文件变得更容易。然而,通过这些方法生成的证书是自签名的,可能不太安全。在您获得使用这些文件的经验后,考虑从注册的证书颁发机构获取证书/密钥材料。

重要

如果连接到 MySQL 服务器实例的客户端使用带有extendedKeyUsage扩展(X.509 v3 扩展)的 SSL 证书,则扩展密钥用途必须包括客户端认证(clientAuth)。如果 SSL 证书仅指定用于服务器认证(serverAuth)和其他非客户端证书目的,证书验证将失败,客户端连接到 MySQL 服务器实例将失败。MySQL 服务器生成的 SSL 证书中没有extendedKeyUsage扩展。如果您使用另一种方式创建的自己的客户端证书,请确保任何extendedKeyUsage扩展包括客户端认证。

  • 自动 SSL 和 RSA 文件生成

  • 使用 mysql_ssl_rsa_setup 手动生成 SSL 和 RSA 文件

  • SSL 和 RSA 文件特性

自动 SSL 和 RSA 文件生成

对于使用 OpenSSL 编译的 MySQL 发行版,MySQL 服务器具有在启动时自动生成缺失的 SSL 和 RSA 文件的能力。auto_generate_certssha256_password_auto_generate_rsa_keyscaching_sha2_password_auto_generate_rsa_keys系统变量控制这些文件的自动生成。这些变量默认启用。它们可以在启动时启用和检查,但不能在运行时设置。

在启动时,如果启用了auto_generate_certs系统变量,没有指定除--ssl之外的其他 SSL 选项,并且数据目录中缺少服务器端 SSL 文件,则服务器会自动在数据目录中生成服务器端和客户端 SSL 证书和密钥文件。这些文件使得可以使用 SSL 加密客户端连接;参见第 8.3.1 节,“配置 MySQL 使用加密连接”。

  1. 服务器检查数据目录中具有以下名称的 SSL 文件:

    ca.pem
    server-cert.pem
    server-key.pem
    
  2. 如果存在任何这些文件,则服务器不会创建 SSL 文件。否则,它会创建它们,以及一些额外的文件:

    ca.pem               Self-signed CA certificate
    ca-key.pem           CA private key
    server-cert.pem      Server certificate
    server-key.pem       Server private key
    client-cert.pem      Client certificate
    client-key.pem       Client private key
    
  3. 如果服务器自动生成 SSL 文件,则使用ca.pemserver-cert.pemserver-key.pem文件的名称来设置相应的系统变量(ssl_cassl_certssl_key)。

在启动时,如果满足以下所有条件,则服务器会自动在数据目录中生成 RSA 私钥/公钥对文件:启用了sha256_password_auto_generate_rsa_keyscaching_sha2_password_auto_generate_rsa_keys系统变量;未指定 RSA 选项;数据目录中缺少 RSA 文件。这些密钥对文件使得可以在通过sha256_passwordcaching_sha2_password插件进行身份验证的帐户之间使用 RSA 在未加密连接上进行安全密码交换;参见第 8.4.1.3 节,“SHA-256 可插拔认证”和第 8.4.1.2 节,“缓存 SHA-2 可插拔认证”。

  1. 服务器检查数据目录中具有以下名称的 RSA 文件:

    private_key.pem      Private member of private/public key pair
    public_key.pem       Public member of private/public key pair
    
  2. 如果存在任何这些文件,则服务器不会创建 RSA 文件。否则,它会创建它们。

  3. 如果服务器自动生成 RSA 文件,则使用它们的名称来设置相应的系统变量(sha256_password_private_key_pathsha256_password_public_key_path; caching_sha2_password_private_key_pathcaching_sha2_password_public_key_path)。

使用 mysql_ssl_rsa_setup 手动生成 SSL 和 RSA 文件

MySQL 发行版包含一个mysql_ssl_rsa_setup实用程序(自 MySQL 8.0.34 起已弃用),可以手动调用以生成 SSL 和 RSA 文件。该实用程序包含在所有 MySQL 发行版中,但需要openssl命令可用。有关使用说明,请参见第 6.4.3 节,“mysql_ssl_rsa_setup — Create SSL/RSA Files”。

SSL 和 RSA 文件特征

服务器自动创建的 SSL 和 RSA 文件或通过调用mysql_ssl_rsa_setup创建的文件具有以下特征:

  • SSL 和 RSA 的位数为 2048 位。

  • SSL CA 证书是自签名的。

  • SSL 服务器和客户端证书使用 CA 证书和密钥签名,使用sha256WithRSAEncryption签名算法。

  • SSL 证书使用这些通用名称(CN)值,具有适当的证书类型(CA、服务器、客户端):

    ca.pem:         MySQL_Server_*suffix*_Auto_Generated_CA_Certificate
    server-cert.pm: MySQL_Server_*suffix*_Auto_Generated_Server_Certificate
    client-cert.pm: MySQL_Server_*suffix*_Auto_Generated_Client_Certificate
    

    suffix值基于 MySQL 版本号。对于由mysql_ssl_rsa_setup生成的文件,可以使用--suffix选项显式指定后缀。

    对于服务器生成的文件,如果结果 CN 值超过 64 个字符,则名称的_*后缀部分将被省略。

  • SSL 文件的国家(C)、州或省(ST)、组织(O)、组织单位名称(OU)和电子邮件地址为空值。

  • 由服务器或mysql_ssl_rsa_setup生成的 SSL 文件从生成时起有效期为十年。

  • RSA 文件不会过期。

  • SSL 文件对于每个证书/密钥对具有不同的序列号(CA 为 1,服务器为 2,客户端为 3)。

  • 服务器自动创建的文件归属于运行服务器的账户。使用 mysql_ssl_rsa_setup 创建的文件归属于调用该程序的用户。在支持 chown() 系统调用的系统上,如果程序由 root 调用并且提供了 --uid 选项来指定应该拥有这些文件的用户,则可以更改这一点。

  • 在 Unix 和类 Unix 系统上,证书文件的文件访问模式为 644(即,全局可读),密钥文件的文件访问模式为 600(即,只能由运行服务器的账户访问)。

要查看 SSL 证书的内容(例如,检查其有效日期范围),可以直接调用 openssl

openssl x509 -text -in ca.pem
openssl x509 -text -in server-cert.pem
openssl x509 -text -in client-cert.pem

使用以下 SQL 语句也可以检查 SSL 证书的过期信息:

mysql> SHOW STATUS LIKE 'Ssl_server_not%';
+-----------------------+--------------------------+
| Variable_name         | Value                    |
+-----------------------+--------------------------+
| Ssl_server_not_after  | Apr 28 14:16:39 2027 GMT |
| Ssl_server_not_before | May  1 14:16:39 2017 GMT |
+-----------------------+--------------------------+

原文:dev.mysql.com/doc/refman/8.0/en/creating-ssl-files-using-openssl.html

8.3.3.2 使用 openssl 创建 SSL 证书和密钥

本节描述了如何使用openssl命令设置供 MySQL 服务器和客户端使用的 SSL 证书和密钥文件。第一个示例显示了一个简化的过程,类似于您可能从命令行中使用的过程。第二个示例显示了一个包含更多细节的脚本。前两个示例适用于 Unix,并且都使用 OpenSSL 的一部分的openssl命令。第三个示例描述了如何在 Windows 上设置 SSL 文件。

注意

有比本文所述过程更简单的生成 SSL 所需文件的替代方法:让服务器自动生成它们或使用mysql_ssl_rsa_setup程序(自 8.0.34 起已弃用)。请参阅第 8.3.3.1 节,“使用 MySQL 创建 SSL 和 RSA 证书和密钥”。

重要

无论您使用何种方法生成证书和密钥文件,用于服务器和客户端证书/密钥的通用名称值必须与用于 CA 证书的通用名称值不同。否则,使用 OpenSSL 编译的服务器的证书和密钥文件将无法工作。在这种情况下的典型错误是:

ERROR 2026 (HY000): SSL connection error:
error:00000001:lib(0):func(0):reason(1)

重要

如果连接到 MySQL 服务器实例的客户端使用带有extendedKeyUsage扩展(X.509 v3 扩展)的 SSL 证书,则扩展密钥用途必须包括客户端认证(clientAuth)。如果 SSL 证书仅指定用于服务器认证(serverAuth)和其他非客户端证书目的,证书验证将失败,客户端连接到 MySQL 服务器实例将失败。在本主题中按照说明使用openssl命令创建的 SSL 证书中没有extendedKeyUsage扩展。如果您使用其他方式创建的自己的客户端证书,请确保任何extendedKeyUsage扩展包括客户端认证。

  • 示例 1:在 Unix 命令行上创建 SSL 文件

  • 示例 2:使用 Unix 脚本创建 SSL 文件

  • 示例 3:在 Windows 上创建 SSL 文件

示例 1:在 Unix 命令行上创建 SSL 文件

以下示例显示了一组命令,用于创建 MySQL 服务器和客户端证书和密钥文件。您必须通过openssl命令回答几个提示。要生成测试文件,可以对所有提示按 Enter 键。要生成用于生产的文件,应提供非空响应。

# Create clean environment
rm -rf newcerts
mkdir newcerts && cd newcerts

# Create CA certificate
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 3600 \
        -key ca-key.pem -out ca.pem

# Create server certificate, remove passphrase, and sign it
# server-cert.pem = public key, server-key.pem = private key
openssl req -newkey rsa:2048 -days 3600 \
        -nodes -keyout server-key.pem -out server-req.pem
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 3600 \
        -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem

# Create client certificate, remove passphrase, and sign it
# client-cert.pem = public key, client-key.pem = private key
openssl req -newkey rsa:2048 -days 3600 \
        -nodes -keyout client-key.pem -out client-req.pem
openssl rsa -in client-key.pem -out client-key.pem
openssl x509 -req -in client-req.pem -days 3600 \
        -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem

生成证书后,请验证它们:

openssl verify -CAfile ca.pem server-cert.pem client-cert.pem

你应该看到如下响应:

server-cert.pem: OK
client-cert.pem: OK

要查看证书的内容(例如,检查证书有效日期范围),可以像这样调用openssl

openssl x509 -text -in ca.pem
openssl x509 -text -in server-cert.pem
openssl x509 -text -in client-cert.pem

现在你有一组文件,可以按以下方式使用:

  • ca.pem:用于在服务器端设置ssl_ca系统变量和客户端端的--ssl-ca选项。(如果使用 CA 证书,则两端必须相同。)

  • server-cert.pemserver-key.pem:用于在服务器端设置ssl_certssl_key系统变量。

  • client-cert.pemclient-key.pem:将这些用作客户端端的--ssl-cert--ssl-key选项的参数。

对于额外的使用说明,请参见第 8.3.1 节,“配置 MySQL 使用加密连接”。

示例 2:在 Unix 上使用脚本创建 SSL 文件

这是一个示例脚本,展示如何为 MySQL 设置 SSL 证书和密钥文件。执行脚本后,按照第 8.3.1 节,“配置 MySQL 使用加密连接”中描述的方式用于 SSL 连接。

DIR=`pwd`/openssl
PRIV=$DIR/private

mkdir $DIR $PRIV $DIR/newcerts
cp /usr/share/ssl/openssl.cnf $DIR
replace ./demoCA $DIR -- $DIR/openssl.cnf

# Create necessary files: $database, $serial and $new_certs_dir
# directory (optional)

touch $DIR/index.txt
echo "01" > $DIR/serial

#
# Generation of Certificate Authority(CA)
#

openssl req -new -x509 -keyout $PRIV/cakey.pem -out $DIR/ca.pem \
    -days 3600 -config $DIR/openssl.cnf

# Sample output:
# Using configuration from /home/jones/openssl/openssl.cnf
# Generating a 1024 bit RSA private key
# ................++++++
# .........++++++
# writing new private key to '/home/jones/openssl/private/cakey.pem'
# Enter PEM pass phrase:
# Verifying password - Enter PEM pass phrase:
# -----
# You are about to be asked to enter information to be
# incorporated into your certificate request.
# What you are about to enter is what is called a Distinguished Name
# or a DN.
# There are quite a few fields but you can leave some blank
# For some fields there will be a default value,
# If you enter '.', the field will be left blank.
# -----
# Country Name (2 letter code) [AU]:FI
# State or Province Name (full name) [Some-State]:.
# Locality Name (eg, city) []:
# Organization Name (eg, company) [Internet Widgits Pty Ltd]:MySQL AB
# Organizational Unit Name (eg, section) []:
# Common Name (eg, YOUR name) []:MySQL admin
# Email Address []:

#
# Create server request and key
#
openssl req -new -keyout $DIR/server-key.pem -out \
    $DIR/server-req.pem -days 3600 -config $DIR/openssl.cnf

# Sample output:
# Using configuration from /home/jones/openssl/openssl.cnf
# Generating a 1024 bit RSA private key
# ..++++++
# ..........++++++
# writing new private key to '/home/jones/openssl/server-key.pem'
# Enter PEM pass phrase:
# Verifying password - Enter PEM pass phrase:
# -----
# You are about to be asked to enter information that will be
# incorporated into your certificate request.
# What you are about to enter is what is called a Distinguished Name
# or a DN.
# There are quite a few fields but you can leave some blank
# For some fields there will be a default value,
# If you enter '.', the field will be left blank.
# -----
# Country Name (2 letter code) [AU]:FI
# State or Province Name (full name) [Some-State]:.
# Locality Name (eg, city) []:
# Organization Name (eg, company) [Internet Widgits Pty Ltd]:MySQL AB
# Organizational Unit Name (eg, section) []:
# Common Name (eg, YOUR name) []:MySQL server
# Email Address []:
#
# Please enter the following 'extra' attributes
# to be sent with your certificate request
# A challenge password []:
# An optional company name []:

#
# Remove the passphrase from the key
#
openssl rsa -in $DIR/server-key.pem -out $DIR/server-key.pem

#
# Sign server cert
#
openssl ca -cert $DIR/ca.pem -policy policy_anything \
    -out $DIR/server-cert.pem -config $DIR/openssl.cnf \
    -infiles $DIR/server-req.pem

# Sample output:
# Using configuration from /home/jones/openssl/openssl.cnf
# Enter PEM pass phrase:
# Check that the request matches the signature
# Signature ok
# The Subjects Distinguished Name is as follows
# countryName           :PRINTABLE:'FI'
# organizationName      :PRINTABLE:'MySQL AB'
# commonName            :PRINTABLE:'MySQL admin'
# Certificate is to be certified until Sep 13 14:22:46 2003 GMT
# (365 days)
# Sign the certificate? [y/n]:y
#
#
# 1 out of 1 certificate requests certified, commit? [y/n]y
# Write out database with 1 new entries
# Data Base Updated

#
# Create client request and key
#
openssl req -new -keyout $DIR/client-key.pem -out \
    $DIR/client-req.pem -days 3600 -config $DIR/openssl.cnf

# Sample output:
# Using configuration from /home/jones/openssl/openssl.cnf
# Generating a 1024 bit RSA private key
# .....................................++++++
# .............................................++++++
# writing new private key to '/home/jones/openssl/client-key.pem'
# Enter PEM pass phrase:
# Verifying password - Enter PEM pass phrase:
# -----
# You are about to be asked to enter information that will be
# incorporated into your certificate request.
# What you are about to enter is what is called a Distinguished Name
# or a DN.
# There are quite a few fields but you can leave some blank
# For some fields there will be a default value,
# If you enter '.', the field will be left blank.
# -----
# Country Name (2 letter code) [AU]:FI
# State or Province Name (full name) [Some-State]:.
# Locality Name (eg, city) []:
# Organization Name (eg, company) [Internet Widgits Pty Ltd]:MySQL AB
# Organizational Unit Name (eg, section) []:
# Common Name (eg, YOUR name) []:MySQL user
# Email Address []:
#
# Please enter the following 'extra' attributes
# to be sent with your certificate request
# A challenge password []:
# An optional company name []:

#
# Remove the passphrase from the key
#
openssl rsa -in $DIR/client-key.pem -out $DIR/client-key.pem

#
# Sign client cert
#

openssl ca -cert $DIR/ca.pem -policy policy_anything \
    -out $DIR/client-cert.pem -config $DIR/openssl.cnf \
    -infiles $DIR/client-req.pem

# Sample output:
# Using configuration from /home/jones/openssl/openssl.cnf
# Enter PEM pass phrase:
# Check that the request matches the signature
# Signature ok
# The Subjects Distinguished Name is as follows
# countryName           :PRINTABLE:'FI'
# organizationName      :PRINTABLE:'MySQL AB'
# commonName            :PRINTABLE:'MySQL user'
# Certificate is to be certified until Sep 13 16:45:17 2003 GMT
# (365 days)
# Sign the certificate? [y/n]:y
#
#
# 1 out of 1 certificate requests certified, commit? [y/n]y
# Write out database with 1 new entries
# Data Base Updated

#
# Create a my.cnf file that you can use to test the certificates
#

cat <<EOF > $DIR/my.cnf
[client]
ssl-ca=$DIR/ca.pem
ssl-cert=$DIR/client-cert.pem
ssl-key=$DIR/client-key.pem
[mysqld]
ssl_ca=$DIR/ca.pem
ssl_cert=$DIR/server-cert.pem
ssl_key=$DIR/server-key.pem
EOF
示例 3:在 Windows 上创建 SSL 文件

如果您的系统尚未安装 OpenSSL,请下载 Windows 版。可在此处查看可用软件包的概述:

http://www.slproweb.com/products/Win32OpenSSL.html

根据您的架构(32 位或 64 位)选择 Win32 OpenSSL Light 或 Win64 OpenSSL Light 软件包。默认安装位置为C:\OpenSSL-Win32C:\OpenSSL-Win64,取决于您下载的软件包。以下说明假定默认位置为C:\OpenSSL-Win32。如果您使用 64 位软件包,则根据需要进行修改。

如果在安装过程中出现消息指示“...关键组件丢失:Microsoft Visual C++ 2019 Redistributables”,请取消安装并根据您的架构(32 位或 64 位)再次下载以下软件包之一:

  • Visual C++ 2008 Redistributables(x86),可在以下位置找到:

    http://www.microsoft.com/downloads/details.aspx?familyid=9B2DA534-3E03-4391-8A4D-074B9F2BC1BF
    
  • Visual C++ 2008 Redistributables(x64),可在以下位置找到:

    http://www.microsoft.com/downloads/details.aspx?familyid=bd2a6171-e2d6-4230-b809-9a8d7548c1b6
    

安装额外的软件包后,重新启动 OpenSSL 设置过程。

在安装过程中,将默认的C:\OpenSSL-Win32作为安装路径,并保持默认选项'将 OpenSSL DLL 文件复制到 Windows 系统目录'被选中。

安装完成后,将C:\OpenSSL-Win32\bin添加到服务器的 Windows 系统路径变量中(根据您的 Windows 版本,以下路径设置说明可能略有不同):

  1. 在 Windows 桌面上,右键单击“我的电脑”图标,然后选择“属性”。

  2. 从出现的系统属性菜单中选择“高级”选项卡,然后点击“环境变量”按钮。

  3. 在系统变量下,选择“Path”,然后点击“编辑”按钮。应该会出现“编辑系统变量”对话框。

  4. 在末尾添加';C:\OpenSSL-Win32\bin'(注意分号)。

  5. 依次按下 3 次“确定”按钮。

  6. 通过打开一个新的命令控制台(开始>运行>cmd.exe)并验证 OpenSSL 是否可用来检查 OpenSSL 是否已正确集成到 Path 变量中:

    Microsoft Windows [Version ...]
    Copyright (c) 2006 Microsoft Corporation. All rights reserved.
    
    C:\Windows\system32>cd \
    
    C:\>openssl
    OpenSSL> exit <<< If you see the OpenSSL prompt, installation was successful.
    
    C:\>
    

安装 OpenSSL 后,使用类似于本节前面示例 1 的指令,但需进行以下更改:

  • 更改以下 Unix 命令:

    # Create clean environment
    rm -rf newcerts
    mkdir newcerts && cd newcerts
    

    在 Windows 上,使用以下命令代替:

    # Create clean environment
    md c:\newcerts
    cd c:\newcerts
    
  • 当命令行末尾显示一个'\'字符时,必须删除此'\'字符,并将命令行输入在同一行上。

生成证书和密钥文件后,要将它们用于 SSL 连接,请参阅第 8.3.1 节,“配置 MySQL 使用加密连接”。

原文:dev.mysql.com/doc/refman/8.0/en/creating-rsa-files-using-openssl.html

8.3.3.3 使用 openssl 创建 RSA 密钥

本节描述如何使用openssl命令设置 RSA 密钥文件,使 MySQL 能够支持通过未加密连接进行安全密码交换的帐户,这些帐户由sha256_passwordcaching_sha2_password插件进行身份验证。

注意

有比本文所述过程更简单的生成 RSA 所需文件的替代方法:让服务器自动生成它们或使用mysql_ssl_rsa_setup程序(自 MySQL 8.0.34 起已弃用)。请参阅 Section 8.3.3.1, “使用 MySQL 创建 SSL 和 RSA 证书和密钥”。

要创建 RSA 私钥和公钥文件,请在登录用于运行 MySQL 服务器的系统帐户时运行以下命令,以便这些文件归该帐户所有:

openssl genrsa -out private_key.pem 2048
openssl rsa -in private_key.pem -pubout -out public_key.pem

这些命令创建了 2,048 位的密钥。要创建更强大的密钥,请使用更大的值。

然后设置密钥文件的访问模式。私钥应仅由服务器可读,而公钥可以自由分发给客户端用户:

chmod 400 private_key.pem
chmod 444 public_key.pem

8.3.4 通过 SSH 从 Windows 远程连接到 MySQL

原文:dev.mysql.com/doc/refman/8.0/en/windows-and-ssh.html

本节描述了如何通过 SSH 与远程 MySQL 服务器建立加密连接。这些信息由 David Carlson 提供 <dcarlson@mplcomm.com>

  1. 在您的 Windows 机器上安装一个 SSH 客户端。有关 SSH 客户端的比较,请参见en.wikipedia.org/wiki/Comparison_of_SSH_clients

  2. 启动您的 Windows SSH 客户端。将Host_Name = *yourmysqlserver_URL_or_IP*设置为您的 MySQL 服务器的 URL 或 IP。将userid=*your_userid*设置为登录到服务器的用户 ID。这个userid值可能与您的 MySQL 账户的用户名不同。

  3. 设置端口转发。可以进行远程转发(设置local_port: 3306remote_host: *yourmysqlservername_or_ip*remote_port: 3306)或本地转发(设置port: 3306host: localhostremote port: 3306)。

  4. 保存所有设置,否则下次必须重新设置。

  5. 使用您刚刚创建的 SSH 会话登录到服务器。

  6. 在您的 Windows 机器上,启动一些 ODBC 应用程序(如 Access)。

  7. 在 Windows 中创建一个新文件,并以与通常相同的方式链接到 MySQL,只是在 MySQL 主机服务器处键入localhost,而不是yourmysqlservername

此时,您应该已经建立了使用 SSH 加密的 MySQL 的 ODBC 连接。

8.3.5 重用 SSL 会话

原文:dev.mysql.com/doc/refman/8.0/en/reusing-ssl-sessions.html

截至 MySQL 8.0.29,MySQL 客户端程序可以选择恢复先前的 SSL 会话,前提是服务器在其运行时缓存中具有该会话。本节描述了有利于 SSL 会话重用的条件,用于管理和监控会话缓存的服务器变量,以及用于存储和重用会话数据的客户端命令行选项。

  • 用于 SSL 会话重用的服务器端运行时配置和监控

  • 用于 SSL 会话重用的客户端端配置

每次完整的 TLS 交换在计算和网络开销方面都可能很昂贵,如果使用 TLSv1.3 则开销较小。通过从已建立的会话中提取会话票证,然后在建立下一个连接时提交该票证,如果会话可以被重用,则总体成本会降低。例如,考虑具有可以打开多个连接并生成更快速度的网页的好处。

通常,在 SSL 会话可以被重用之前,必须满足以下条件:

  • 服务器必须将其会话缓存保留在内存中。

  • 服务器端会话缓存超时时间不能已过期。

  • 每个客户端都必须维护一个活动会话的缓存并保持其安全。

C 应用程序可以利用 C API 功能来启用加密连接的会话重用(参见 SSL 会话重用)。

用于 SSL 会话重用的服务器端运行时配置和监控

为了创建初始的 TLS 上下文,服务器使用启动时上下文相关系统变量的值。为了暴露上下文值,服务器还初始化了一组相应的状态变量。以下表格显示了定义服务器运行时会话缓存的系统变量以及暴露当前活动会话缓存值的相应状态变量。

表 8.15 用于会话重用的系统和状态变量

系统变量名 对应的状态变量名
ssl_session_cache_mode Ssl_session_cache_mode
ssl_session_cache_timeout Ssl_session_cache_timeout

注意

ssl_session_cache_mode服务器变量的值为ON时,这是默认模式,Ssl_session_cache_mode状态变量的值为SERVER

SSL 会话缓存变量适用于mysql_mainmysql_admin TLS 通道。它们的值也作为性能模式tls_channel_status表中的属性公开,以及任何其他活动 TLS 上下文的属性。

要在运行时重新配置 SSL 会话缓存,请使用以下步骤:

  1. 将应更改为其新值的每个与缓存相关的系统变量设置为其新值。例如,将缓存超时值从默认值(300 秒)更改为 600 秒:

    mysql> SET GLOBAL ssl_session_cache_timeout = 600;
    

    每对系统和状态变量的成员可能由于重新配置过程的方式而暂时具有不同的值。

    mysql> SHOW VARIABLES LIKE 'ssl_session_cache_timeout';
    +---------------------------+-------+
    | Variable_name             | Value |
    +---------------------------+-------+
    | ssl_session_cache_timeout | 600   |
    +---------------------------+-------+
    1 row in set (0.00 sec)
    
    mysql> SHOW STATUS LIKE 'Ssl_session_cache_timeout';
    +---------------------------+-------+
    | Variable_name             | Value |
    +---------------------------+-------+
    | Ssl_session_cache_timeout | 300   |
    +---------------------------+-------+
    1 row in set (0.00 sec)
    

    有关设置变量值的其他信息,请参阅系统变量赋值。

  2. 执行ALTER INSTANCE RELOAD TLS。此语句会根据缓存相关系统变量的当前值重新配置活动的 TLS 上下文。它还会将缓存相关状态变量设置为反映新活动缓存值。该语句需要CONNECTION_ADMIN权限。

    mysql> ALTER INSTANCE RELOAD TLS;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> SHOW VARIABLES LIKE 'ssl_session_cache_timeout';
    +---------------------------+-------+
    | Variable_name             | Value |
    +---------------------------+-------+
    | ssl_session_cache_timeout | 600   |
    +---------------------------+-------+
    1 row in set (0.00 sec)
    
    mysql> SHOW STATUS LIKE 'Ssl_session_cache_timeout';
    +---------------------------+-------+
    | Variable_name             | Value |
    +---------------------------+-------+
    | Ssl_session_cache_timeout | 600   |
    +---------------------------+-------+
    1 row in set (0.00 sec)
    

    在执行ALTER INSTANCE RELOAD TLS后建立的新连接使用新的 TLS 上下文。现有连接不受影响。

SSL 会话重用的客户端端配置

所有 MySQL 客户端程序都能够重用先前会话,用于与同一服务器建立的新加密连接,前提是您在原始连接仍处于活动状态时存储了会话数据。会话数据存储到文件中,当您再次调用客户端时,���会指定此文件。

要存储和重用 SSL 会话数据,请使用以下步骤:

  1. 调用mysql以建立到运行 MySQL 8.0.29 或更高版本的服务器的加密连接。

  2. 使用ssl_session_data_print命令指定一个文件路径,您可以在其中安全地存储当前活动会话数据。例如:

    mysql> ssl_session_data_print ~/private-dir/session.txt
    

    会话数据以以空终止的、PEM 编码的 ANSI 字符串形式获取。如果省略路径和文件名,则该字符串将打印到标准输出。

  3. 从命令解释器的提示符中,调用任何 MySQL 客户端程序以建立到同一服务器的新加密连接。要重用会话数据,请指定--ssl-session-data命令行选项和文件参数。

    例如,使用mysql建立一个新连接:

    mysql -u admin -p --ssl-session-data=~/private-dir/session.txt
    

    然后使用mysqlshow客户端:

    mysqlshow -u admin -p --ssl-session-data=~/private-dir/session.txt
    Enter password: *****
    +--------------------+
    |     Databases      |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    | world              |
    +--------------------+
    

    在每个示例中,客户端尝试恢复原始会话,同时与同一服务器建立新连接。

    要确认mysql是否重用了会话,请查看status命令的输出。如果当前活动的mysql连接确实恢复了会话,则状态信息包括SSL 会话重用: true

除了mysqlmysqlshow外,SSL 会话重用也适用于mysqladminmysqlbinlogmysqlcheckmysqldumpmysqlimportmysqlpumpmysqlslapmysqltestmysql_migrate_keyringmysql_secure_installationmysql_upgrade

几种情况可能会阻止成功检索会话数据。例如,如果会话未完全连接、不是 SSL 会话、服务器尚未发送会话数据或 SSL 会话无法重用。即使有正确存储的会话数据,服务器的会话缓存也可能超时。无论原因是什么,如果指定了--ssl-session-data,但会话无法重用,则默认情况下会返回错误。例如:

mysqlshow -u admin -p --ssl-session-data=~/private-dir/session.txt
Enter password: *****
ERROR:
--ssl-session-data specified but the session was not reused.

为了抑制错误消息,并通过在命令行上静默创建一个新会话来建立连接,需要指定--ssl-session-data-continue-on-failed-reuse,以及--ssl-session-data。如果服务器的缓存超时已过期,可以将会话数据再次存储到同一文件中。默认服务器缓存超时可以延长(参见 SSL 会话重用的服务器端运行时配置和监控)。

8.4 安全组件和插件

原文:dev.mysql.com/doc/refman/8.0/en/security-plugins.html

8.4.1 认证插件

8.4.2 连接控制插件

8.4.3 密码验证组件

8.4.4 MySQL 密钥环

8.4.5 MySQL 企业审计

8.4.6 审计消息组件

8.4.7 MySQL 企业防火墙

MySQL 包括几个实现安全功能的组件和插件:

  • 用于验证客户端连接到 MySQL 服务器的插件。有几种认证协议的插件可用。有关认证过程的一般讨论,请参见 第 8.2.17 节,“可插拔认证”。有关特定认证插件的特性,请参见 第 8.4.1 节,“认证插件”。

  • 用于实施密码强度策略和评估潜在密码强度的密码验证组件。参见 第 8.4.3 节,“密码验证组件”。

  • 提供安全存储敏感信息的密钥环插件。参见 第 8.4.4 节,“MySQL 密钥环”。

  • (仅适用于 MySQL 企业版)MySQL 企业审计,使用服务器插件实现,使用开放的 MySQL 审计 API,可以对在特定 MySQL 服务器上执行的连接和查询活动进行标准、基于策略的监视和记录。设计符合 Oracle 审计规范,MySQL 企业审计为受内部和外部监管指导的应用程序提供了一个开箱即用、易于使用的审计和合规解决方案。参见 第 8.4.5 节,“MySQL 企业审计”。

  • 一个函数使应用程序能够将自己的消息事件添加到审计日志中。参见 第 8.4.6 节,“审计消息组件”。

  • (仅适用于 MySQL 企业版)MySQL 企业防火墙,一种应用级防火墙,使数据库管理员能够根据匹配接受的语句模式列表来允许或拒绝 SQL 语句执行。这有助于加固 MySQL 服务器,防止 SQL 注入等攻击或利用应用程序以其合法查询工作负载特征之外的方式。参见 第 8.4.7 节,“MySQL 企业防火墙”。

  • (仅适用于 MySQL 企业版) MySQL 企业数据脱敏和去标识化,实现为一个包含插件和一组函数的插件库。数据脱敏通过用替代值替换真实值来隐藏敏感信息。MySQL 企业数据脱敏和去标识化函数可以使用多种方法对现有数据进行脱敏,例如混淆(删除识别特征)、生成格式化的随机数据以及数据替换或替代。请参阅第 8.5 节,“MySQL 企业数据脱敏和去标识化”。

- 8.4.1 认证插件

原文:dev.mysql.com/doc/refman/8.0/en/authentication-plugins.html

8.4.1.1 本地可插拔认证

8.4.1.2 缓存 SHA-2 可插拔认证

8.4.1.3 SHA-256 可插拔认证

8.4.1.4 客户端明文可插拔认证

8.4.1.5 PAM 可插拔认证

8.4.1.6 Windows 可插拔认证

8.4.1.7 LDAP 可插拔认证

8.4.1.8 Kerberos 可插拔认证

8.4.1.9 无登录可插拔认证

8.4.1.10 套接字对等凭证可插拔认证

8.4.1.11 FIDO 可插拔认证

8.4.1.12 测试可插拔认证

8.4.1.13 可插拔认证系统变量

注意

如果您正在寻找关于authentication_oci插件的信息,这仅适用于 MySQL HeatWave 服务。请参阅authentication_oci 插件,在MySQL HeatWave Service手册中。

以下各节描述了 MySQL 中可用的可插拔认证方法以及实现这些方法的插件。有关认证过程的一般讨论,请参阅第 8.2.17 节,“可插拔认证”。

  • 默认认证插件的确定方式如默认认证插件中所述。

原文:dev.mysql.com/doc/refman/8.0/en/native-pluggable-authentication.html

8.4.1.1 本地可插拔认证

MySQL 包含一个实现本地认证的mysql_native_password插件;即,基于在可插拔认证引入之前使用的密码哈希方法的认证。

注意

从 MySQL 8.0.34 开始,mysql_native_password认证插件已被弃用,并可能在将来的 MySQL 版本中被移除。

以下表显示了服务器端和客户端端的插件名称。

表 8.16 本地密码认证的插件和库名称

插件或文件 插件或文件名
服务器端插件 mysql_native_password
客户端插件 mysql_native_password
库文件 无(插件已内置)

以下各节提供了特定于本地可插拔认证的安装和使用信息:

  • 安装本地可插拔认证

  • 使用本地可插拔认证

有关 MySQL 中可插拔认证的一般信息,请参阅第 8.2.17 节,“可插拔认证”。

安装本地可插拔认证

mysql_native_password插件存在于服务器和客户端形式中:

  • 服务器端插件内置于服务器中,无需显式加载,并且无法通过卸载来禁用它。

  • 客户端插件内置于libmysqlclient客户端库中,并可供链接到libmysqlclient的任何程序使用。

使用本地可插拔认证

MySQL 客户端程序默认使用mysql_native_password--default-auth选项可用作程序可以期望使用的客户端插件的提示:

$> mysql --default-auth=mysql_native_password ...

原文:dev.mysql.com/doc/refman/8.0/en/caching-sha2-pluggable-authentication.html

8.4.1.2 缓存 SHA-2 可插拔身份验证

MySQL 提供了两个实现 SHA-256 哈希用于用户帐户密码的身份验证插件:

  • sha256_password:实现基本的 SHA-256 身份验证。

  • caching_sha2_password:实现 SHA-256 身份验证(类似于sha256_password),但在服务器端使用缓存以提高性能,并具有更广泛适用性的附加功能。

本节描述了缓存 SHA-2 身份验证插件。有关原始基本(非缓存)插件的信息,请参见第 8.4.1.3 节,“SHA-256 可插拔身份验证”。

重要

在 MySQL 8.0 中,caching_sha2_password是默认的身份验证插件,而不是mysql_native_password。有关此更改对服务器操作以及服务器与客户端和连接器的兼容性的影响的信息,请参见将 caching_sha2_password 作为首选身份验证插件。

重要

要使用使用caching_sha2_password插件进行身份验证的帐户连接到服务器,必须使用安全连接或支持使用 RSA 密钥对进行密码交换的未加密连接,如本节后面所述。无论哪种方式,caching_sha2_password插件都使用 MySQL 的加密功能。参见第 8.3 节,“使用加密连接”。

注意

sha256_password中,“sha256”指的是插件用于加密的 256 位摘要长度。在caching_sha2_password中,“sha2”更普遍地指 SHA-2 类加密算法,256 位加密是其中的一个实例。后者的名称选择为未来扩展可能的摘要长度留出了空间,而无需更改插件名称。

sha256_password相比,caching_sha2_password插件具有以下优点:

  • 在服务器端,内存中的缓存使得已连接过的用户再次连接时能够更快地重新验证身份。

  • 基于 RSA 的密码交换可用,无论 MySQL 链接到哪个 SSL 库。

  • 支持使用 Unix 套接字文件和共享内存协议的客户端连接。

以下表格显示了服务器端和客户端的插件名称。

表 8.17 SHA-2 身份验证的插件和库名称

插件或文件 插件或文件名
服务器端插件 caching_sha2_password
客户端插件 caching_sha2_password
库文件 无(插件内置)

以下各节提供了特定于缓存 SHA-2 可插拔身份验证的安装和使用信息:

  • 安装 SHA-2 可插拔身份验证

  • 使用 SHA-2 可插拔身份验证

  • SHA-2 可插拔身份验证的缓存操作

有关 MySQL 中可插拔身份验证的一般信息,请参阅第 8.2.17 节,“可插拔身份验证”。

安装 SHA-2 可插拔身份验证

caching_sha2_password插件以服务器和客户端形式存在:

  • 服务器端插件内置于服务器中,无需显式加载,并且无法通过卸载来禁用。

  • 客户端插件内置于libmysqlclient客户端库中,并可供链接到libmysqlclient的任何程序使用。

服务器端插件使用sha2_cache_cleaner审计插件作为辅助工具来执行密码缓存管理。sha2_cache_cleanercaching_sha2_password一样,是内置的,无需安装。

使用 SHA-2 可插拔身份验证

要设置一个使用caching_sha2_password插件进行 SHA-256 密码哈希的帐户,请使用以下语句,其中password是所需的帐户密码:

CREATE USER 'sha2user'@'localhost'
IDENTIFIED WITH caching_sha2_password BY '*password*';

服务器将caching_sha2_password插件分配给帐户,并使用它使用 SHA-256 加密密码,将这些值存储在mysql.user系统表的pluginauthentication_string列中。

前述说明并不假设caching_sha2_password是默认的身份验证插件。如果caching_sha2_password是默认的身份验证插件,则可以使用更简单的CREATE USER语法。

要将服务器的默认身份验证插件设置为caching_sha2_password,请在服务器选项文件中添加以下行:

[mysqld]
default_authentication_plugin=caching_sha2_password

这将导致caching_sha2_password插件默认用于新帐户。因此,可以创建帐户并设置其密码而无需显式命名插件:

CREATE USER 'sha2user'@'localhost' IDENTIFIED BY '*password*';

default_authentication_plugin设置为caching_sha2_password的另一个后果是,要使用其他插件进行帐户创建,必须明确指定该插件。例如,要使用mysql_native_password插件,请使用以下语句:

CREATE USER 'nativeuser'@'localhost'
IDENTIFIED WITH mysql_native_password BY '*password*';

caching_sha2_password 支持通过安全传输进行连接。如果您按照本节后面给出的 RSA 配置过程,它还支持在未加密连接上使用 RSA 进行加密密码交换。RSA 支持具有以下特点:

  • 在服务器端,两个系统变量命名了 RSA 私钥和公钥对文件:caching_sha2_password_private_key_pathcaching_sha2_password_public_key_path。如果要使用与系统变量默认值不同的名称的密钥文件,则数据库管理员必须在服务器启动时设置这些变量。

  • 服务器使用 caching_sha2_password_auto_generate_rsa_keys 系统变量来确定是否自动生成 RSA 密钥对文件。请参阅 Section 8.3.3, “Creating SSL and RSA Certificates and Keys”。

  • Caching_sha2_password_rsa_public_key 状态变量显示了 caching_sha2_password 认证插件使用的 RSA 公钥值。

  • 拥有 RSA 公钥的客户端可以在连接过程中与服务器执行基于 RSA 密钥对的密码交换,如后面所述。

  • 对于使用 caching_sha2_password 和 RSA 密钥对进行密码交换进行身份验证的帐户的连接,默认情况下服务器不会向客户端发送 RSA 公钥。客户端可以使用所需公钥的客户端副本,或者从服务器请求公钥。

    使用受信任的本地公钥副本使客户端能够避免在客户端/服务器协议中的往返,并且比从服务器请求公钥更安全。另一方面,从服务器请求公钥更方便(不需要管理客户端文件)并且在安全网络环境中可能是可接受的。

    • 对于命令行客户端,请使用--server-public-key-path选项来指定 RSA 公钥文件。使用--get-server-public-key选项从服务器请求公钥。以下程序支持这两个选项:mysqlmysqlshmysqladminmysqlbinlogmysqlcheckmysqldumpmysqlimportmysqlpumpmysqlshowmysqlslapmysqltestmysql_upgrade

    • 对于使用 C API 的程序,通过调用mysql_options()来指定 RSA 公钥文件,传递MYSQL_SERVER_PUBLIC_KEY选项和文件名,或者通过传递MYSQL_OPT_GET_SERVER_PUBLIC_KEY选项从服务器请求公钥。

    • 对于副本,使用CHANGE REPLICATION SOURCE TO语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO语句(在 MySQL 8.0.23 之前)与SOURCE_PUBLIC_KEY_PATH | MASTER_PUBLIC_KEY_PATH选项来指定 RSA 公钥文件,或使用GET_SOURCE_PUBLIC_KEY | GET_MASTER_PUBLIC_KEY选项从源请求公钥。对于组复制,group_replication_recovery_public_key_pathgroup_replication_recovery_get_public_key系统变量具有相同的作用。

    在所有情况下,如果给定指定有效公钥文件的选项,则该选项优先于从服务器请求公钥的选项。

对于使用caching_sha2_password插件的客户端,在连接到服务器时,密码永远不会以明文形式暴露。密码传输的方式取决于是否使用安全连接或 RSA 加密:

  • 如果连接是安全的,则不需要使用 RSA 密钥对。这适用于使用 TLS 加密的 TCP 连接,以及 Unix 套接字文件和共享内存连接。密码以明文形式发送,但由于连接是安全的,无法窃听。

  • 如果连接不安全,则使用 RSA 密钥对。这适用于未使用 TLS 加密的 TCP 连接和命名管道连接。RSA 仅用于客户端和服务器之间的密码交换,以防止密码窗口。当服务器接收到加密密码时,它会对其进行解密。加密中使用了混淆以防止重复攻击。

要在客户端连接过程中使用 RSA 密钥对进行密码交换,请使用以下步骤:

  1. 使用 Section 8.3.3, “Creating SSL and RSA Certificates and Keys”中的说明创建 RSA 私钥和公钥对文件。

  2. 如果私钥和公钥文件位于数据目录中,并且命名为private_key.pempublic_key.pemcaching_sha2_password_private_key_pathcaching_sha2_password_public_key_path系统变量的默认值),服务器将在启动时自动使用它们。

    否则,要显式命名密钥文件,请在服务器选项文件中将系统变量设置为密钥文件名。如果文件位于服务器数据目录中,则无需指定其完整路径名:

    [mysqld]
    caching_sha2_password_private_key_path=myprivkey.pem
    caching_sha2_password_public_key_path=mypubkey.pem
    

    如果密钥文件不位于数据目录中,或者要在系统变量值中明确指定其位置,请使用完整路径名:

    [mysqld]
    caching_sha2_password_private_key_path=/usr/local/mysql/myprivkey.pem
    caching_sha2_password_public_key_path=/usr/local/mysql/mypubkey.pem
    
  3. 如果要更改caching_sha2_password在密码生成过程中使用的哈希轮数,请设置caching_sha2_password_digest_rounds系统变量。例如:

    [mysqld]
    caching_sha2_password_digest_rounds=10000
    
  4. 重新启动服务器,然后连接到服务器并检查Caching_sha2_password_rsa_public_key状态变量值。实际显示的值与此处显示的值不同,但应为非空:

    mysql> SHOW STATUS LIKE 'Caching_sha2_password_rsa_public_key'\G
    *************************** 1\. row ***************************
    Variable_name: Caching_sha2_password_rsa_public_key
            Value: -----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDO9nRUDd+KvSZgY7cNBZMNpwX6
    MvE1PbJFXO7u18nJ9lwc99Du/E7lw6CVXw7VKrXPeHbVQUzGyUNkf45Nz/ckaaJa
    aLgJOBCIDmNVnyU54OT/1lcs2xiyfaDMe8fCJ64ZwTnKbY2gkt1IMjUAB5Ogd5kJ
    g8aV7EtKwyhHb0c30QIDAQAB
    -----END PUBLIC KEY-----
    

    如果值为空,则服务器发现密钥文件存在问题。检查错误日志以获取诊断信息。

在服务器配置了 RSA 密钥文件后,使用caching_sha2_password插件进行身份验证的帐户可以选择使用这些密钥文件连接到服务器。如前所述,这些帐户可以使用安全连接(在这种情况下不使用 RSA)或使用 RSA 执行密码交换的未加密连接。假设使用未加密连接。例如:

$> mysql --ssl-mode=DISABLED -u sha2user -p
Enter password: *password*

对于 sha2user 的此连接尝试,服务器确定 caching_sha2_password 是适当的认证插件并调用它(因为在 CREATE USER 时指定了该插件)。插件发现连接未加密,因此需要使用 RSA 加密传输密码。然而,服务器不会将公钥发送给客户端,客户端也未提供公钥,因此无法加密密码,连接失败:

ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password'
reported error: Authentication requires secure connection.

要从服务器请求 RSA 公钥,请指定 --get-server-public-key 选项:

$> mysql --ssl-mode=DISABLED -u sha2user -p --get-server-public-key
Enter password: *password*

在这种情况下,服务器将 RSA 公钥发送给客户端,客户端使用它加密密码并将结果返回给服务器。插件在服务器端使用 RSA 私钥解密密码,并根据密码是否正确接受或拒绝连接。

或者,如果客户端有包含服务器所需的 RSA 公钥的本地副本的文件,则可以使用 --server-public-key-path 选项指定该文件:

$> mysql --ssl-mode=DISABLED -u sha2user -p --server-public-key-path=*file_name*
Enter password: *password*

在这种情况下,客户端使用公钥加密密码并将结果返回给服务器。插件在服务器端使用 RSA 私钥解密密码,并根据密码是否正确接受或拒绝连接。

--server-public-key-path 选项指定的文件中的公钥值应与由 caching_sha2_password_public_key_path 系统变量指定的服务器端文件中的密钥值相同。如果密钥文件包含有效的公钥值但该值不正确,则会出现访问被拒绝的错误。如果密钥文件不包含有效的公钥,则客户端程序无法使用它。

客户端用户可以通过两种方式获取 RSA 公钥:

  • 数据库管理员可以提供公钥文件的副本。

  • 可以连接到服务器的客户端用户可以使用 SHOW STATUS LIKE 'Caching_sha2_password_rsa_public_key' 语句并将返回的密钥值保存在文件中。

SHA-2 可插拔认证的缓存操作

在服务器端,caching_sha2_password 插件使用内存中的缓存来更快地对先前连接过的客户端进行认证。条目由账户名/密码哈希对组成。缓存的工作方式如下:

  1. 当客户端连接时,caching_sha2_password 检查客户端和密码是否与某个缓存条目匹配。如果匹配,则认证成功。

  2. 如果没有匹配的缓存条目,插件会尝试根据mysql.user系统表中的凭证验证客户端。如果成功,caching_sha2_password会为客户端添加一个条目到哈希中。否则,认证失败并拒绝连接。

这样,当客户端首次连接时,会对mysql.user系统表进行认证。当客户端随后连接时,会对缓存进行更快的认证。

除了添加条目之外的密码缓存操作由sha2_cache_cleaner审计插件处理,该插件代表caching_sha2_password执行这些操作:

  • 它清除任何被重命名或删除的账户的缓存条目,或者更改了凭证或认证插件的账户。

  • 当执行FLUSH PRIVILEGES语句时,会清空缓存。

  • 在服务器关闭时会清空缓存。(这意味着缓存在服务器重新启动时不是持久的。)

清空缓存操作会影响后续客户端连接的认证要求。对于每个用户账户,在以下任何操作之后,用户的第一个客户端连接必须使用安全连接(使用 TLS 凭证的 TCP 连接,Unix 套接字文件或共享内存)或基于 RSA 密钥对的密码交换:

  • 在账户创建后。

  • 在账户密码更改后。

  • RENAME USER后的账户。

  • FLUSH PRIVILEGES后。

FLUSH PRIVILEGES清空整个缓存并影响所有使用caching_sha2_password插件的账户。其他操作清空特定缓存条目并仅影响操作中的账户。

一旦用户成功认证,账户将被输入缓存,后续连接不需要安全连接或 RSA 密钥对,直到发生影响该账户的另一个清空缓存事件。(当可以使用缓存时,服务器使用一种不使用明文密码传输且不需要安全连接的挑战-响应机制。)

原文:dev.mysql.com/doc/refman/8.0/en/sha256-pluggable-authentication.html

8.4.1.3 SHA-256 可插拔认证

MySQL 提供了两个认证插件,用于实现 SHA-256 哈希算法对用户账户密码进行加密:

  • sha256_password:实现基本的 SHA-256 认证。

  • caching_sha2_password:实现 SHA-256 认证(类似于sha256_password),但在服务器端使用缓存以提高性能,并具有更广泛适用性的附加功能。

本节描述了原始的非缓存 SHA-2 认证插件。有关缓存插件的信息,请参见第 8.4.1.2 节,“缓存 SHA-2 可插拔认证”。

重要提示

在 MySQL 8.0 中,默认认证插件为caching_sha2_password而不是mysql_native_password。有关此更改对服务器操作以及服务器与客户端和连接器的兼容性的影响的信息,请参见 caching_sha2_password 作为首选认证插件。

由于caching_sha2_password是 MySQL 8.0 中的默认认证插件,并提供了sha256_password认证插件的全部功能,因此sha256_password已被弃用;预计在未来的 MySQL 版本中将其移除。使用sha256_password进行认证的 MySQL 账户应迁移到使用caching_sha2_password

重要提示

要使用使用sha256_password插件进行认证连接到服务器,您必须使用支持密码交换的 TLS 连接或 RSA 密钥对的非加密连接,如本节后面描述的那样。无论哪种方式,sha256_password插件都使用 MySQL 的加密功能。参见第 8.3 节,“使用加密连接”。

注意

sha256_password中,“sha256”指的是插件用于加密的 256 位摘要长度。在caching_sha2_password中,“sha2”更普遍地指 SHA-2 类加密算法,256 位加密是其中的一个实例。后者的名称选择为未来扩展可能的摘要长度留出了空间,而不更改插件名称。

以下表格显示了服务器端和客户端端的插件名称。

表 8.18 SHA-256 认证的插件和库名称

插件或文件 插件或文件名
服务器端插件 sha256_password
客户端端插件 sha256_password
库文件 无(插件已内置)

以下各节提供了特定于 SHA-256 可插拔认证的安装和使用信息:

  • 安装 SHA-256 可插拔认证

  • 使用 SHA-256 可插拔认证

有关 MySQL 中可插拔认证的一般信息,请参见第 8.2.17 节,“可插拔认证”。

安装 SHA-256 可插拔认证

sha256_password插件以服务器和客户端形式存在:

  • 服务器端插件内置于服务器中,无需显式加载,并且无法通过卸载来禁用。

  • 客户端端插件内置于libmysqlclient客户端库中,并可供链接到libmysqlclient的任何程序使用。

使用 SHA-256 可插拔认证

要设置使用sha256_password插件进行 SHA-256 密码哈希的帐户,请使用以下语句,其中password是所需的帐户密码:

CREATE USER 'sha256user'@'localhost'
IDENTIFIED WITH sha256_password BY '*password*';

服务器将sha256_password插件分配给帐户,并使用它使用 SHA-256 加密密码,将这些值存储在mysql.user系统表的pluginauthentication_string列中。

上述说明不假设sha256_password是默认认证插件。如果sha256_password是默认认证插件,则可以使用更简单的CREATE USER语法。

要将服务器的默认认证插件设置为sha256_password启动服务器,请在服务器选项文件中添加以下行:

[mysqld]
default_authentication_plugin=sha256_password

这将导致默认情况下新帐户使用sha256_password插件。因此,可以创建帐户并设置其密码而无需明确命名插件:

CREATE USER 'sha256user'@'localhost' IDENTIFIED BY '*password*';

default_authentication_plugin设置为sha256_password的另一个后果是,要使用其他插件进行帐户创建,必须明确指定该插件。例如,要使用mysql_native_password插件,请使用以下语句:

CREATE USER 'nativeuser'@'localhost'
IDENTIFIED WITH mysql_native_password BY '*password*';

sha256_password支持通过安全传输进行连接。如果 MySQL 使用 OpenSSL 编译,并且要连接的 MySQL 服务器配置为支持 RSA(使用本节后面给出的 RSA 配置过程),sha256_password还支持在未加密连接上使用 RSA 进行加密密码交换。

RSA 支持具有以下特征:

  • 在服务器端,两个系统变量命名 RSA 私钥和公钥对文件:sha256_password_private_key_pathsha256_password_public_key_path。如果要使用与系统变量默认值不同的名称的密钥文件,数据库管理员必须在服务器启动时设置这些变量。

  • 服务器使用sha256_password_auto_generate_rsa_keys系统变量来确定是否自动生成 RSA 密钥对文件。请参阅第 8.3.3 节,“创建 SSL 和 RSA 证书和密钥”。

  • Rsa_public_key状态变量显示sha256_password认证插件使用的 RSA 公钥值。

  • 拥有 RSA 公钥的客户端可以在连接过程中与服务器执行基于 RSA 密钥对的密码交换,如后文所述。

  • 对于使用sha256_password和 RSA 密钥对进行密码交换进行身份验证的帐户的连接,服务器根据需要向客户端发送 RSA 公钥。但是,如果客户端主机上有公钥的副本,则客户端可以使用它来节省客户端/服务器协议中的往返:

    • 对于这些命令行客户端,请使用--server-public-key-path选项来指定 RSA 公钥文件:mysql, mysqladmin, mysqlbinlog, mysqlcheck, mysqldump, mysqlimport, mysqlpump, mysqlshow, mysqlslap, mysqltest, mysql_upgrade.

    • 对于使用 C API 的程序,请调用mysql_options()来通过传递MYSQL_SERVER_PUBLIC_KEY选项和文件名指定 RSA 公钥文件。

    • 对于副本,使用CHANGE REPLICATION SOURCE TO语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO语句(在 MySQL 8.0.23 之前)与SOURCE_PUBLIC_KEY_PATH | MASTER_PUBLIC_KEY_PATH选项来指定 RSA 公钥文件。对于组复制,group_replication_recovery_get_public_key系统变量具有相同的作用。

对于使用sha256_password插件的客户端,在连接到服务器时,密码永远不会以明文形式暴露。密码传输的方式取决于是否使用安全连接或 RSA 加密:

  • 如果连接是安全的,则不需要使用 RSA 密钥对,也不会使用。这适用于使用 TLS 加密的连接。密码以明文形式发送,但无法被窃听,因为连接是安全的。

    注意

    caching_sha2_password不同,sha256_password插件不将共享内存连接视为安全,即使共享内存传输默认安全。

  • 如果连接不安全,并且存在 RSA 密钥对,则连接保持未加密。这适用于未使用 TLS 加密的连接。RSA 仅用于客户端和服务器之间的密码交换,以防止密码窃听。当服务器接收到加密密码时,它会对其进行解密。在加密中使用了一种混淆以防止重复攻击。

  • 如果未使用安全连接且不可用 RSA 加密,则连接尝试将失败,因为密码无法在未以明文形式暴露的情况下发送。

注意

要使用sha256_password进行 RSA 密码加密,客户端和服务器都必须使用 OpenSSL 进行编译,而不仅仅是其中一个。

假设 MySQL 已使用 OpenSSL 编译,请使用以下过程在客户端连接过程中启用 RSA 密钥对用于密码交换:

  1. 使用 Section 8.3.3, “Creating SSL and RSA Certificates and Keys”中的说明创建 RSA 私钥和公钥对文件。

  2. 如果私钥和公钥文件位于数据目录中,并且命名为private_key.pempublic_key.pemsha256_password_private_key_pathsha256_password_public_key_path系统变量的默认值),服务器将在启动时自动使用它们。

    否则,要显式命名关键文件,请在服务器选项文件中设置系统变量为关键文件名。如果文件位于服务器数据目录中,则无需指定其完整路径名:

    [mysqld]
    sha256_password_private_key_path=myprivkey.pem
    sha256_password_public_key_path=mypubkey.pem
    

    如果密钥文件不位于数据目录中,或者要在系统变量值中明确它们的位置,请使用完整路径名:

    [mysqld]
    sha256_password_private_key_path=/usr/local/mysql/myprivkey.pem
    sha256_password_public_key_path=/usr/local/mysql/mypubkey.pem
    
  3. 重新启动服务器,然后连接到服务器并检查Rsa_public_key状态变量值。实际显示的值与此处显示的值不同,但应为非空:

    mysql> SHOW STATUS LIKE 'Rsa_public_key'\G
    *************************** 1\. row ***************************
    Variable_name: Rsa_public_key
            Value: -----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDO9nRUDd+KvSZgY7cNBZMNpwX6
    MvE1PbJFXO7u18nJ9lwc99Du/E7lw6CVXw7VKrXPeHbVQUzGyUNkf45Nz/ckaaJa
    aLgJOBCIDmNVnyU54OT/1lcs2xiyfaDMe8fCJ64ZwTnKbY2gkt1IMjUAB5Ogd5kJ
    g8aV7EtKwyhHb0c30QIDAQAB
    -----END PUBLIC KEY-----
    

    如果值为空,则服务器发现密钥文件存在问题。请检查错误日志以获取诊断信息。

在服务器配置了 RSA 密钥文件之后,使用sha256_password插件进行身份验证的帐户可以选择使用这些密钥文件连接到服务器。如前所述,这些帐户可以使用安全连接(在这种情况下不使用 RSA)或使用 RSA 执行密码交换的未加密连接。假设使用未加密连接。例如:

$> mysql --ssl-mode=DISABLED -u sha256user -p
Enter password: *password*

对于sha256user的此连接尝试,服务器确定sha256_password是适当的身份验证插件并调用它(因为在CREATE USER时指定了该插件)。插件发现连接未加密,因此需要使用 RSA 加密传输密码。在这种情况下,插件将 RSA 公钥发送给客户端,客户端使用它加密密码并将结果返回给服务器。插件在服务器端使用 RSA 私钥解密密码,并根据密码是否正确接受或拒绝连接。

服务器根据需要向客户端发送 RSA 公钥。但是,如果客户端有包含服务器所需的 RSA 公钥的本地副本的文件,可以使用--server-public-key-path选项指定该文件:

$> mysql --ssl-mode=DISABLED -u sha256user -p --server-public-key-path=*file_name*
Enter password: *password*

--server-public-key-path选项指定的文件中的公钥值应与由sha256_password_public_key_path系统变量指定的服务器端文件中的密钥值相同。如果密钥文件包含有效的公钥值但值不正确,则会发生访问被拒绝的错误。如果密钥文件不包含有效的公钥,则客户端程序无法使用它。在这种情况下,sha256_password插件将向客户端发送公钥,就好像没有指定--server-public-key-path选项一样。

客户端用户可以通过两种方式获取 RSA 公钥:

  • 数据库管理员可以提供公钥文件的副本。

  • 客户端用户可以通过其他方式连接到服务器,使用SHOW STATUS LIKE 'Rsa_public_key'语句并将返回的密钥值保存在文件中。

原文:dev.mysql.com/doc/refman/8.0/en/cleartext-pluggable-authentication.html

8.4.1.4 客户端端明文可插拔身份验证

有一个客户端身份验证插件可用,使客户端可以将密码以明文形式发送到服务器,而无需进行哈希或加密。此插件内置于 MySQL 客户端库中。

以下表格显示了插件名称。

Table 8.19 Plugin and Library Names for Cleartext Authentication

插件或文件 插件或文件名
服务器端插件 无,请参见讨论
客户端插件 mysql_clear_password
库文件 无(插件内置)

许多客户端身份验证插件在客户端将密码发送到服务器之前对密码进行哈希或加密。这使客户端可以避免以明文形式发送密码。

对于需要服务器接收客户端输入的密码的身份验证方案,无法进行哈希或加密。在这种情况下,将使用客户端端的mysql_clear_password插件,该插件使客户端可以将密码以明文形式发送到服务器。没有相应的服务器端插件。相反,mysql_clear_password可以与需要明文密码的任何服务器端插件一起在客户端端使用(例如 PAM 和简单的 LDAP 身份验证插件;请参见 Section 8.4.1.5, “PAM Pluggable Authentication”,以及 Section 8.4.1.7, “LDAP Pluggable Authentication”)。

下面的讨论提供了特定于明文插件身份验证的使用信息。有关 MySQL 中可插拔身份验证的一般信息,请参见 Section 8.2.17, “Pluggable Authentication”。

注意

在某些配置中,以明文形式发送密码可能会带来安全问题。为避免问题,如果存在密码可能被拦截的可能性,客户端应该使用一种保护密码的方法连接到 MySQL 服务器。可能的方法包括 SSL(请参见 Section 8.3, “Using Encrypted Connections”)、IPsec 或私有网络。

为了减少意外使用mysql_clear_password插件的可能性,MySQL 客户端必须显式启用它。可以通过几种方式实现:

  • LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN环境变量设置为以1Yy开头的值。这将为所有客户端连接启用该插件。

  • mysql, mysqladmin, mysqlcheck, mysqldump, mysqlshow, 以及 mysqlslap 客户端程序支持 --enable-cleartext-plugin 选项,可以在每次调用时启用该插件。

  • mysql_options() C API 函数支持 MYSQL_ENABLE_CLEARTEXT_PLUGIN 选项,可以在每个连接上启用该插件。此外,任何使用 libmysqlclient 并读取选项文件的程序都可以通过在客户端库读取的选项组中包含 enable-cleartext-plugin 选项来启用该插件。

原文:dev.mysql.com/doc/refman/8.0/en/pam-pluggable-authentication.html

8.4.1.5 PAM 可插入认证

注意

PAM 可插入认证是 MySQL 企业版中包含的扩展,是一款商业产品。要了解更多关于商业产品的信息,请参阅www.mysql.com/products/

MySQL 企业版支持一种认证方法,使得 MySQL 服务器能够使用 PAM(可插入认证模块)来认证 MySQL 用户。PAM 使得系统能够使用标准接口访问各种认证方法,例如传统的 Unix 密码或 LDAP 目录。

PAM 可插入认证提供了以下功能:

  • 外部认证:PAM 认证使得 MySQL 服务器能够接受来自 MySQL 授权表之外定义的用户的连接,并使用 PAM 支持的方法进行认证。

  • 代理用户支持:PAM 认证可以根据外部用户所属的 PAM 组和提供的认证字符串,返回一个与客户端程序传递的外部用户名不同的 MySQL 用户名,这意味着插件可以返回定义外部 PAM 认证用户应具有的特权的 MySQL 用户。例如,名为joe的操作系统用户可以连接并具有名为developer的 MySQL 用户的权限。

PAM 可插入认证已在 Linux 和 macOS 上进行了测试;请注意 Windows 不支持 PAM。

以下表格显示了插件和库文件名称。文件名后缀可能在您的系统上有所不同。该文件必须位于由plugin_dir系统变量命名的目录中。有关安装信息,请参阅安装 PAM 可插入认证。

表 8.20 PAM 认证的插件和库名称

插件或文件 插件或文件名
服务器端插件 authentication_pam
客户端插件 mysql_clear_password
库文件 authentication_pam.so

与服务器端 PAM 插件通信的客户端mysql_clear_password明文插件内置于libmysqlclient客户端库中,并包含在所有发行版中,包括社区发行版。在所有 MySQL 发行版中包含客户端明文插件使得来自任何发行版的客户端都能连接到加载了服务器端 PAM 插件的服务器。

以下部分提供了特定于 PAM 可插入认证的安装和使用信息:

  • MySQL 用户 PAM 认证工作原理

  • 安装 PAM 可插拔认证

  • 卸载 PAM 可插拔认证

  • 使用 PAM 可插拔认证

  • 无代理用户的 PAM Unix 密码认证

  • 无代理用户的 PAM LDAP 认证

  • 带有代理用户和组映射的 PAM Unix 密码认证

  • 访问 Unix 密码存储的 PAM 认证

  • PAM 认证调试

关于 MySQL 中可插拔认证的一般信息,请参见第 8.2.17 节,“可插拔认证”。关于mysql_clear_password插件的信息,请参见第 8.4.1.4 节,“客户端明文可插拔认证”。有关代理用户信息,请参见第 8.2.19 节,“代理用户”。

MySQL 用户的 PAM 认证工作原理

本节概述了 MySQL 和 PAM 如何共同工作来认证 MySQL 用户。有关如何设置 MySQL 帐户以使用特定 PAM 服务的示例,请参见使用 PAM 可插拔认证。

  1. 客户端程序和服务器进行通信,客户端向服务器发送客户端用户名(默认为操作系统用户名)和密码:

    • 客户端用户名为外部用户名。

    • 对于使用 PAM 服务器端认证插件的帐户,相应的客户端插件是mysql_clear_password。这个客户端插件不执行密码哈希处理,结果客户端将密码以明文形式发送到服务器。

  2. 服务器根据客户端连接的外部用户名和主机找到匹配的 MySQL 帐户。PAM 插件使用 MySQL 服务器传递给它的信息(如用户名、主机名、密码和认证字符串)。当您定义一个使用 PAM 进行身份验证的 MySQL 帐户时,认证字符串包含:

    • PAM 服务名称,这是系统管理员可以用来引用特定应用程序的身份验证方法的名称。可以将多个应用程序关联到单个数据库服务器实例,因此服务名称的选择留给 SQL 应用程序开发人员。

    • 可选地,如果要使用代理,可以从 PAM 组到 MySQL 用户名的映射。

  3. 该插件使用认证字符串中命名的 PAM 服务来检查用户凭据,并返回'Authentication succeeded, Username is *user_name*''Authentication failed'。密码必须适用于 PAM 服务使用的密码存储。示例:

    • 对于传统的 Unix 密码,服务查找存储在/etc/shadow文件中的密码。

    • 对于 LDAP,服务查找存储在 LDAP 目录中的密码。

    如果凭据检查失败,服务器将拒绝连接。

  4. 否则,认证字符串指示是否进行代理。如果字符串不包含 PAM 组映射,则不会进行代理。在这种情况下,MySQL 用户名与外部用户名相同。

  5. 否则,根据 PAM 组映射指示代理,MySQL 用户名根据映射列表中的第一个匹配组确定。“PAM 组”的含义取决于 PAM 服务。示例:

    • 对于传统的 Unix 密码,组是在/etc/group文件中定义的 Unix 组,可能在诸如/etc/security/group.conf之类的文件中补充了其他 PAM 信息。

    • 对于 LDAP,组是在 LDAP 目录中定义的 LDAP 组。

    如果代理用户(外部用户)对被代理的 MySQL 用户名具有PROXY权限,则进行代理,代理用户承担被代理用户的权限。

安装 PAM 可插拔认证

本节描述了如何安装服务器端 PAM 认证插件。有关安装插件的一般信息,请参见第 7.6.1 节,“安装和卸载插件”。

要使服务器能够使用插件库文件,必须将其位于 MySQL 插件目录中(由plugin_dir系统变量命名的目录)。如果需要,通过在服务器启动时设置plugin_dir的值来配置插件目录位置。

插件库文件基本名称为authentication_pam,通常编译为.so后缀。

要在服务器启动时加载插件,请使用--plugin-load-add选项命名包含插件的库文件。使用此插件加载方法,每次启动服务器都必须提供该选项。例如,在服务器my.cnf文件中放入以下行:

[mysqld]
plugin-load-add=authentication_pam.so

修改my.cnf后,重新启动服务器以使新设置生效。

或者,要在运行时加载插件,请使用以下语句,根据需要调整.so后缀:

INSTALL PLUGIN authentication_pam SONAME 'authentication_pam.so';

INSTALL PLUGIN立即加载插件,并在mysql.plugins系统表中注册它,以使服务器在每次后续正常启动时加载它,而无需--plugin-load-add

要验证插件安装,请检查信息模式PLUGINS表或使用SHOW PLUGINS语句(参见 Section 7.6.2, “Obtaining Server Plugin Information”)。例如:

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS
       FROM INFORMATION_SCHEMA.PLUGINS
       WHERE PLUGIN_NAME LIKE '%pam%';
+--------------------+---------------+
| PLUGIN_NAME        | PLUGIN_STATUS |
+--------------------+---------------+
| authentication_pam | ACTIVE        |
+--------------------+---------------+

如果插件初始化失败,请检查服务器错误日志以获取诊断信息。

要将 MySQL 帐户与 PAM 插件关联,请参阅使用 PAM 可插拔认证。

卸载 PAM 可插拔认证

卸载 PAM 认证插件的方法取决于您安装插件的方式:

  • 如果您使用--plugin-load-add选项在服务器启动时安装了插件,请在不带该选项的情况下重新启动服务器。

  • 如果您使用INSTALL PLUGIN语句在运行时安装了插件,则在服务器重新启动时仍保留安装。要卸载它,请使用UNINSTALL PLUGIN

    UNINSTALL PLUGIN authentication_pam;
    
使用 PAM 可插拔认证

本节概述了如何使用 PAM 认证插件从 MySQL 客户端程序连接到服务器。以下各节提供了使用 PAM 认证的具体方法的说明。假定服务器正在运行并启用了服务器端 PAM 插件,如安装 PAM 可插拔认证中所述。

要在CREATE USER语句的IDENTIFIED WITH子句中引用 PAM 认证插件,请使用名称authentication_pam。例如:

CREATE USER *user*
  IDENTIFIED WITH authentication_pam
  AS '*auth_string*';

认证字符串指定以下类型的信息:

  • PAM 服务名称(参见 MySQL 用户的 PAM 认证工作原理)。以下讨论中的示例使用mysql-unix作为服务名称,用于使用传统 Unix 密码进行身份验证,以及mysql-ldap用于使用 LDAP 进行身份验证。

  • 对于代理支持,PAM 提供了一种方式,让 PAM 模块在连接到服务器时,返回一个与客户端程序传递的外部用户名不同的 MySQL 用户名。使用认证字符串来控制从外部用户名到 MySQL 用户名的映射。如果要利用代理用户功能,认证字符串必须包含这种映射。

例如,如果一个账户使用mysql-unix PAM 服务名称,并且应该将rootusers PAM 组中的操作系统用户映射到developerdata_entry MySQL 用户,可以使用类似以下语句:

CREATE USER *user*
  IDENTIFIED WITH authentication_pam
  AS 'mysql-unix, root=developer, users=data_entry';

PAM 认证插件的认证字符串语法遵循以下规则:

  • 该字符串由 PAM 服务名称组成,可选择地后跟一个 PAM 组映射列表,其中包含一个或多个关键字/值对,每个对指定一个 PAM 组名和一个 MySQL 用户名:

    *pam_service_name*[,*pam_group_name*=*mysql_user_name*]...
    

    插件会解析每个使用该账户的连接尝试的认证字符串。为了最小化开销,尽量保持字符串尽可能短。

  • 每个*pam_group_name*=*mysql_user_name*对前面必须有一个逗号。

  • 不在双引号内的前导和尾随空格会被忽略。

  • 未引用的pam_service_namepam_group_namemysql_user_name值可以包含除等号、逗号或空格之外的任何内容。

  • 如果pam_service_namepam_group_namemysql_user_name值用双引号引起来,引号之间的所有内容都是值的一部分。例如,如果值包含空格字符,则这是必要的。所有字符都是合法的,除了双引号和反斜杠(\)。要包含这两个字符,用反斜杠进行转义。

如果插件成功验证外部用户名(客户端传递的名称),它会查找认证字符串中的 PAM 组映射列表,并且如果存在,则根据外部用户所属的 PAM 组返回不同的 MySQL 用户名给 MySQL 服务器:

  • 如果认证字符串不包含 PAM 组映射列表,则插件返回外部名称。

  • 如果认证字符串包含 PAM 组映射列表,则插件会从左到右检查列表中的每个*pam_group_name*=*mysql_user_name*对,并尝试在已认证用户分配的非 MySQL 目录中找到pam_group_name值的匹配项,并返回找到的第一个匹配项的mysql_user_name。如果插件找不到任何 PAM 组的匹配项,则返回外部名称。如果插件无法在目录中查找组,则会忽略 PAM 组映射列表并返回外部名称。

以下各节描述了如何设置使用 PAM 身份验证插件的几种身份验证方案:

  • 没有代理用户。这仅使用 PAM 来检查登录名和密码。每个被允许连接到 MySQL 服务器的外部用户都应该有一个匹配的 MySQL 帐户,该帐户被定义为使用 PAM 身份验证。(为了使 MySQL 帐户'*user_name*'@'*host_name*'与外部用户匹配,user_name必须是外部用户名,host_name必须与客户端连接的主机匹配。)认证可以通过各种 PAM 支持的方法进行。后续讨论将展示如何使用传统的 Unix 密码和 LDAP 密码来验证客户端凭据。

    PAM 身份验证,如果不通过代理用户或 PAM 组进行,需要 MySQL 用户名与操作系统用户名相同。 MySQL 用户名限制为 32 个字符(参见第 8.2.3 节,“授权表”),这限制了 PAM 非代理身份验证仅限于最多 32 个字符的 Unix 帐户。

  • 仅代理用户,带有 PAM 组映射。对于这种情况,创建一个或多个定义不同权限集的 MySQL 帐户。(理想情况下,没有人应该直接使用这些帐户连接。)然后定义一个通过 PAM 进行身份验证的默认用户,使用某种映射方案(通常基于用户所属的外部 PAM 组)将所有外部用户名映射到持有权限集的少数 MySQL 帐户。任何连接并指定外部用户名作为客户端用户名的客户端都将映射到一个 MySQL 帐户并使用其权限。本讨论将展示如何使用传统的 Unix 密码设置此功能,但也可以使用其他 PAM 方法,如 LDAP。

这些方案的变体是可能的:

  • 您可以允许一些用户直接登录(无需代理),但要求其他用户通过代理帐户连接。

  • 您可以通过在 PAM 身份验证帐户之间使用不同的 PAM 服务名称,为一些用户使用一种 PAM 身份验证方法,为其他用户使用另一种方法。例如,您可以为一些用户使用mysql-unix PAM 服务,为其他用户使用mysql-ldap

示例做出以下假设。如果您的系统设置不同,您可能需要进行一些调整。

  • 登录名和密码分别为antonioantonio_password。请将其更改为要进行身份验证的用户。

  • PAM 配置目录为/etc/pam.d

  • PAM 服务名称对应于认证方法(在本讨论中为mysql-unixmysql-ldap)。要使用给定的 PAM 服务,您必须在 PAM 配置目录中设置一个同名的 PAM 文件(如果不存在则创建该文件)。此外,您必须在使用该 PAM 服务进行认证的任何账户的CREATE USER语句的认证字符串中命名 PAM 服务。

PAM 认证插件在初始化时检查服务器启动环境中是否设置了AUTHENTICATION_PAM_LOG环境值。如果是,则插件会启用将诊断消息记录到标准输出的功能。根据服务器的启动方式,消息可能会显示在控制台上或错误日志中。这些消息对于调试描相关问题很有帮助。有关更多信息,请参阅 PAM 认证调试。

无代理用户的 PAM Unix 密码认证

这种认证场景使用 PAM 来检查以操作系统用户名和 Unix 密码定义的外部用户,而无需代理。每个被允许连接到 MySQL 服务器的外部用户都应该有一个匹配的 MySQL 账户,该账户被定义为通过传统的 Unix 密码存储使用 PAM 认证。

注意

传统的 Unix 密码是使用/etc/shadow文件进行检查的。有关与该文件相关的可能问题的信息,请参阅 PAM 认证访问 Unix 密码存储。

  1. 验证 Unix 认证是否允许使用用户名antonio和密码antonio_password登录到操作系统。

  2. 通过创建名为/etc/pam.d/mysql-unixmysql-unix PAM 服务文件来设置 PAM 以使用传统的 Unix 密码对 MySQL 连接进行认证。文件内容取决于系统,因此请检查/etc/pam.d目录中现有的与登录相关的文件以查看其外观。在 Linux 上,mysql-unix文件可能如下所示:

    #%PAM-1.0
    auth            include         password-auth
    account         include         password-auth
    

    对于 macOS,请使用login而不是password-auth

    在某些系统上,PAM 文件格式可能有所不同。例如,在 Ubuntu 和其他基于 Debian 的系统上,使用以下文件内容:

    @include common-auth
    @include common-account
    @include common-session-noninteractive
    
  3. 创建一个与操作系统用户名相同的 MySQL 账户,并定义其使用 PAM 插件和mysql-unix PAM 服务进行认证:

    CREATE USER 'antonio'@'localhost'
      IDENTIFIED WITH authentication_pam
      AS 'mysql-unix';
    GRANT ALL PRIVILEGES
      ON mydb.*
      TO 'antonio'@'localhost';
    

    在这里,认证字符串仅包含 PAM 服务名称,mysql-unix,用于验证 Unix 密码。

  4. 使用mysql命令行客户端作为antonio连接到 MySQL 服务器。例如:

    $> mysql --user=antonio --password --enable-cleartext-plugin
    Enter password: *antonio_password*
    

    服务器应允许连接,并且以下查询返回如下输出:

    mysql> SELECT USER(), CURRENT_USER(), @@proxy_user;
    +-------------------+-------------------+--------------+
    | USER()            | CURRENT_USER()    | @@proxy_user |
    +-------------------+-------------------+--------------+
    | antonio@localhost | antonio@localhost | NULL         |
    +-------------------+-------------------+--------------+
    

    这表明antonio操作系统用户经过认证具有授予antonio MySQL 用户的权限,并且没有发生代理。

注意

客户端端的mysql_clear_password认证插件不会更改密码,因此客户端程序会将其以明文形式发送到 MySQL 服务器。这使得密码可以原样传递给 PAM。在某些配置中,明文密码是使用服务器端 PAM 库所必需的,但可能会存在安全问题。以下措施可最小化风险:

  • 为了减少意外使用mysql_clear_password插件的可能性,MySQL 客户端必须显式启用它(例如,使用--enable-cleartext-plugin选项)。参见 Section 8.4.1.4, “Client-Side Cleartext Pluggable Authentication”。

  • 为了避免启用mysql_clear_password插件时密码暴露,MySQL 客户端应该使用加密连接连接到 MySQL 服务器。参见 Section 8.3.1, “Configuring MySQL to Use Encrypted Connections”。

无代理用户的 PAM LDAP 认证

这种认证场景使用 PAM 来检查以操作系统用户名和 LDAP 密码定义的外部用户,而无需代理。每个被允许连接到 MySQL 服务器的外部用户都应该有一个匹配的 MySQL 账户,该账户被定义为通过 LDAP 进行 PAM 认证。

要使用 PAM LDAP 可插拔认证为 MySQL,必须满足以下先决条件:

  • PAM LDAP 服务必须与 LDAP 服务器通信。

  • 每个要由 MySQL 进行认证的 LDAP 用户必须存在于由 LDAP 服务器管理的目录中。

注意

另一种使用 LDAP 进行 MySQL 用户认证的方法是使用特定于 LDAP 的认证插件。参见 Section 8.4.1.7, “LDAP Pluggable Authentication”。

配置 MySQL 以进行 PAM LDAP 认证如下:

  1. 验证 Unix 认证是否允许使用用户名antonio和密码antonio_password登录到操作系统。

  2. 通过创建一个名为/etc/pam.d/mysql-ldapmysql-ldap PAM 服务文件来设置 PAM 以使用 LDAP 认证 MySQL 连接。文件内容取决于系统,因此请检查/etc/pam.d目录中现有的与登录相关的文件以查看其内容。在 Linux 上,mysql-ldap文件可能如下所示:

    #%PAM-1.0
    auth        required    pam_ldap.so
    account     required    pam_ldap.so
    

    如果在您的系统上,PAM 对象文件的后缀与.so不同,请替换正确的后缀。

    PAM 文件格式可能在某些系统上有所不同。

  3. 创建一个与操作系统用户名相同的 MySQL 账户,并定义其使用 PAM 插件和mysql-ldap PAM 服务进行验证:

    CREATE USER 'antonio'@'localhost'
      IDENTIFIED WITH authentication_pam
      AS 'mysql-ldap';
    GRANT ALL PRIVILEGES
      ON mydb.*
      TO 'antonio'@'localhost';
    

    这里,认证字符串仅包含 PAM 服务名称mysql-ldap,用 LDAP 进行验证。

  4. 连接到服务器的方式与 PAM Unix 密码认证无代理用户中描述的相同。

使用代理用户和组映射的 PAM Unix 密码认证

此处描述的认证方案使用代理和 PAM 组映射,将使用 PAM 进行验证的连接 MySQL 用户映射到定义了不同权限集的其他 MySQL 账户上。用户不直接通过定义权限的账户连接。相反,他们通过使用 PAM 进行身份验证的默认代理账户连接,以便将所有外部用户映射到具有权限的 MySQL 账户。使用代理账户连接的任何用户都将映射到这些 MySQL 账户之一,其权限确定了允许外部用户执行的数据库操作。

此处所示的过程使用 Unix 密码认证。要改用 LDAP,请参阅 PAM LDAP 认证无代理用户的早期步骤。

注意

传统的 Unix 密码使用/etc/shadow文件进行检查。有关与该文件相关的可能问题的信息,请参阅 PAM 认证访问 Unix 密码存储。

  1. 验证 Unix 认证是否允许使用用户名antonio和密码antonio_password登录操作系统。

  2. 验证antonio是否是rootusers PAM 组的成员。

  3. 通过创建名为/etc/pam.d/mysql-unix的文件,设置 PAM 通过操作系统用户验证mysql-unix PAM 服务。文件内容取决于系统,因此请查看/etc/pam.d目录中现有的与登录相关的文件以了解其内容。在 Linux 上,mysql-unix文件可能如下所示:

    #%PAM-1.0
    auth            include         password-auth
    account         include         password-auth
    

    对于 macOS,请使用login而不是password-auth

    在某些系统上,PAM 文件格式可能有所不同。例如,在 Ubuntu 和其他基于 Debian 的系统上,使用以下文件内容:

    @include common-auth
    @include common-account
    @include common-session-noninteractive
    
  4. 创建一个默认的代理用户(''@''),将外部 PAM 用户映射到代理账户:

    CREATE USER ''@''
      IDENTIFIED WITH authentication_pam
      AS 'mysql-unix, root=developer, users=data_entry';
    

    这里,认证字符串包含了 PAM 服务名称mysql-unix,用于验证 Unix 密码。认证字符串还将rootusers PAM 组中的外部用户映射到developerdata_entry MySQL 用户名。

    在设置代理用户时,必须在 PAM 服务名称后面提供 PAM 组映射列表。否则,插件无法确定如何将外部用户名映射到正确的被代理 MySQL 用户名。

    注意

    如果您的 MySQL 安装中存在匿名用户,则可能会与默认代理用户发生冲突。有关此问题的更多信息以及处理方法,请参见 Default Proxy User and Anonymous User Conflicts。

  5. 创建被代理账户并为每个账户授予应具有的权限:

    CREATE USER 'developer'@'localhost'
      IDENTIFIED WITH mysql_no_login;
    CREATE USER 'data_entry'@'localhost'
      IDENTIFIED WITH mysql_no_login;
    
    GRANT ALL PRIVILEGES
      ON mydevdb.*
      TO 'developer'@'localhost';
    GRANT ALL PRIVILEGES
      ON mydb.*
      TO 'data_entry'@'localhost';
    

    代理账户使用mysql_no_login认证插件,以防止客户端直接使用这些账户登录到 MySQL 服务器。相反,使用 PAM 进行身份验证的用户应该通过代理使用基于他们的 PAM 组的developerdata_entry账户。(假设插件已安装。有关说明,请参见 Section 8.4.1.9, “No-Login Pluggable Authentication”。)有关保护代理账户免受直接使用的替代方法,请参见 Preventing Direct Login to Proxied Accounts。

  6. 为每个被代理账户授予PROXY权限:

    GRANT PROXY
      ON 'developer'@'localhost'
      TO ''@'';
    GRANT PROXY
      ON 'data_entry'@'localhost'
      TO ''@'';
    
  7. 使用mysql命令行客户端作为安东尼奥连接到 MySQL 服务器。

    $> mysql --user=antonio --password --enable-cleartext-plugin
    Enter password: *antonio_password*
    

    服务器使用默认的''@''代理账户对连接进行身份验证。安东尼奥的权限取决于他是哪些 PAM 组的成员。如果安东尼奥root PAM 组的成员,PAM 插件将root映射到developer MySQL 用户名并将该名称返回给服务器。服务器验证''@''是否具有developerPROXY权限,并允许连接。以下查询返回如下输出:

    mysql> SELECT USER(), CURRENT_USER(), @@proxy_user;
    +-------------------+---------------------+--------------+
    | USER()            | CURRENT_USER()      | @@proxy_user |
    +-------------------+---------------------+--------------+
    | antonio@localhost | developer@localhost | ''@''        |
    +-------------------+---------------------+--------------+
    

    这表明安东尼奥操作系统用户被验证具有授予developer MySQL 用户的权限,并且通过默认代理账户进行代理。

    如果安东尼奥不是root PAM 组的成员,但是users PAM 组的成员,类似的过程会发生,但插件将user PAM 组成员映射到data_entry MySQL 用户名并将该名称返回给服务器:

    mysql> SELECT USER(), CURRENT_USER(), @@proxy_user;
    +-------------------+----------------------+--------------+
    | USER()            | CURRENT_USER()       | @@proxy_user |
    +-------------------+----------------------+--------------+
    | antonio@localhost | data_entry@localhost | ''@''        |
    +-------------------+----------------------+--------------+
    

    这表明安东尼奥操作系统用户被验证具有data_entry MySQL 用户的权限,并且通过默认代理账户进行代理。

注意

客户端端的mysql_clear_password认证插件保持密码不变,因此客户端程序将其以明文形式发送到 MySQL 服务器。这使得密码可以原样传递给 PAM。在某些配置中,明文密码是使用服务器端 PAM 库所必需的,但可能在某些配置中存在安全问题。以下措施可最小化风险:

  • 为了减少意外使用mysql_clear_password插件的可能性,MySQL 客户端必须显式启用它(例如,使用--enable-cleartext-plugin选项)。参见 Section 8.4.1.4, “Client-Side Cleartext Pluggable Authentication”。

  • 启用mysql_clear_password插件以避免密码暴露,MySQL 客户端应该使用加密连接连接到 MySQL 服务器。参见 Section 8.3.1, “Configuring MySQL to Use Encrypted Connections”。

PAM 身份验证访问 Unix 密码存储

在某些系统上,Unix 身份验证使用密码存储,例如/etc/shadow,这是一个通常具有受限访问权限的文件。这可能导致 MySQL 基于 PAM 的身份验证失败。不幸的是,PAM 实现不允许区分“密码无法检查”(例如,由于无法读取/etc/shadow)和“密码不匹配”。如果您正在使用 Unix 密码存储进行 PAM 身份验证,您可以通过以下方法之一启用 MySQL 对其的访问:

  • 假设 MySQL 服务器是从mysql操作系统帐户运行的,请将该帐户放入具有/etc/shadow访问权限的shadow组中:

    1. /etc/group中创建���个shadow组。

    2. mysql操作系统用户添加到/etc/group中的shadow组。

    3. /etc/group分配给shadow组并启用组读权限:

      chgrp shadow /etc/shadow
      chmod g+r /etc/shadow
      
    4. 重新启动 MySQL 服务器。

  • 如果您正在使用pam_unix模块和unix_chkpwd实用程序,请按以下方式启用密码存储访问:

    chmod u-s /usr/sbin/unix_chkpwd
    setcap cap_dac_read_search+ep /usr/sbin/unix_chkpwd
    

    根据您的平台调整unix_chkpwd的路径。

PAM 身份验证调试

PAM 身份验证插件在初始化时检查AUTHENTICATION_PAM_LOG环境值是否已设置。在 MySQL 8.0.35 及更早版本中,该值无关紧要。如果是,则插件将启用将诊断消息记录到标准输出的功能。这些消息可能有助于调试插件执行身份验证时出现的与 PAM 相关的问题。您应该知道,在这些版本中,这些消息中包含密码。

从 MySQL 8.0.36 开始,设置AUTHENTICATION_PAM_LOG=1(或其他任意值)会产生相同的诊断消息,但包含任何密码。如果您希望在这些消息中包含密码,请设置AUTHENTICATION_PAM_LOG=PAM_LOG_WITH_SECRET_INFO

一些消息包含对 PAM 插件源文件和行号的引用,这使得插件操作与其发生的代码位置更紧密地联系在一起。

用于调试连接失败并确定连接尝试期间发生的情况的另一种技术是配置 PAM 认证以允许所有连接,然后检查系统日志文件。这种技术应仅在临时基础上使用,而不是在生产服务器上使用。

使用以下内容配置名为 /etc/pam.d/mysql-any-password 的 PAM 服务文件(在某些系统上格式可能有所不同):

#%PAM-1.0
auth        required    pam_permit.so
account     required    pam_permit.so

创建一个使用 PAM 插件并命名为 mysql-any-password PAM 服务的帐户:

CREATE USER 'testuser'@'localhost'
  IDENTIFIED WITH authentication_pam
  AS 'mysql-any-password';

mysql-any-password 服务文件会导致任何身份验证尝试返回 true,即使密码不正确。如果身份验证尝试失败,这说明配置问题在 MySQL 方面。否则,问题在操作系统/PAM 方面。要查看可能发生的情况,请检查系统日志文件,如 /var/log/secure/var/log/audit.log/var/log/syslog/var/log/messages

确定问题后,删除 mysql-any-password PAM 服务文件以禁用任意密码访问。

posted @ 2024-06-23 16:24  绝不原创的飞龙  阅读(13)  评论(0编辑  收藏  举报