Pluggable Authentication(PAM,即可插拔式认证模块)是一种高效且灵活的用户级别的认证方式,广泛应用于现代操作系统,特别是Linux服务器中。它允许数据库管理员(DBAs)为MySQL用户帐户选择和更改不同的认证方法。
当客户端尝试连接到MySQL服务器时,服务器会使用客户端提供的用户名和主机名从mysql.user
系统表中查找相应的帐户行。一旦找到该帐户,服务器就会根据帐户行中指定的认证插件来认证客户端。
- 当MySQL服务器无法找到指定的认证插件时,确实会发生一个错误,并且客户端的连接尝试会被拒绝。这是因为服务器依赖于认证插件来验证用户的凭据(通常是用户名和密码)并确定用户是否有权连接到MySQL服务器。
- 如果服务器能够找到并加载认证插件,那么它会调用该插件来进行用户认证。一旦认证插件完成了它的工作,它会向服务器返回一个状态,指示用户是否提供了正确的凭据并且被允许连接。如果状态表明用户通过了认证,那么服务器就会接受这个连接,并允许用户进行后续的操作。如果认证插件返回的状态表明用户没有通过认证(比如密码错误),那么服务器会拒绝连接尝试,并且可能会向客户端返回一个错误消息,指示认证失败的原因。
可插拔身份验证实现了以下重要功能:
- 选择认证方法:不同的认证插件可以实现不同的认证方法,如基于密码的认证、基于证书的认证等。通过使用不同的认证插件,DBAs可以为不同的MySQL帐户选择最适合的认证方法。
- 外部认证:除了存储在
mysql.user
表中的凭据外,某些认证插件还支持使用存储在外部系统(如PAM、Windows登录ID、LDAP或Kerberos)中的凭据进行认证。这使得MySQL可以与现有的身份验证系统集成,无需在MySQL内部存储和管理所有凭据。 - 代理用户:某些认证插件还支持代理用户的概念。在这种情况下,一个用户(代理用户)可以代表另一个用户(被代理用户)进行连接。在连接期间,代理用户将被视为具有被代理用户的权限。这可以用于实现如审计、多租户或其他需要用户之间权限委托的场景。
注意:
当您使用--skip-grant-tables
选项启动MySQL服务器时,服务器会跳过权限表的加载,这意味着它不会使用mysql.user
表或任何其他权限表来验证客户端的连接请求。因此,即使认证插件已经加载,它们也不会被用于客户端的身份验证。
为了减轻这种安全风险,当您使用--skip-grant-tables
选项时,MySQL服务器默认还会启用skip_networking
选项。这意味着服务器将不接受来自远程主机的连接,只接受来自运行MySQL服务器的主机(localhost或127.0.0.1)的连接。
1 可用的身份验证插件
MySQL 8.0提供了以下身份验证插件:
-
执行本地身份验证的插件
在 MySQL 引入可插拔认证(Pluggable Authentication)之前,mysql_native_password
是默认的认证方法,它使用了特定的密码哈希和验证机制。
具体来说,mysql_native_password
插件使用了 MySQL 特定的密码哈希和验证算法。当客户端尝试连接到 MySQL 服务器时,它会发送用户名和密码。服务器使用 mysql_native_password
插件来验证密码。
从MySQL 8.0.34开始,MySQL_native_password身份验证插件已被弃用,并将在未来版本的
MySQL中被删除。
-
使用SHA-256密码哈希进行身份验证的插件提供了比本地身份验证更强的加密。
- 一个客户端插件,用于在不进行哈希或加密的情况下将密码发送到服务器。此插件与服务器端插件一起使用,服务器端插件需要访问客户端用户提供的密码。
- 一个插件,使用PAM(可插拔身份验证模块)执行外部身份验证,使MySQL Server能够使用PAM对MySQL用户进行身份验证。这个插件也支持代理用户。
- 一个在Windows上执行外部身份验证的插件,使MySQL Server能够使用本机Windows服务来验证客户端连接。登录到Windows的用户可以根据其环境中的信息从MySQL客户端程序连接到服务器,而无需指定额外的密码。这个插件也支持代理用户。
- 使用LDAP(轻量级目录访问协议)执行身份验证的插件,通过访问X.500等目录服务对MySQL用户进行身份验证。这些插件也支持代理用户。
- 一个插件,使用Kerberos执行身份验证,以验证与Kerberos主体相对应的MySQL用户。
- 阻止所有客户端连接到任何使用它的帐户的插件。此插件的使用情况包括代理帐户,这些帐户永远不应该允许直接登录,但只能通过代理帐户访问,并且这些帐户必须能够以提升的权限执行存储的程序和视图,而不会将这些权限暴露给普通用户。
- 一个插件,用于验证通过Unix套接字文件从本地主机连接的客户端。
- 一个插件,使用FIDO身份验证将用户身份验证到MySQL Server。从MySQL 8.0.35开始,authentication_fido和authentication_fildo_client身份验证插件已弃用,并将在未来版本的MySQL中删除。
- 一个测试插件,用于检查帐户凭据并将成功或失败记录到服务器错误日志中。此插件用于测试和开发目的,并作为如何编写身份验证插件的示例。
2 默认身份验证插件
CREATE USER和ALTER 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_policy或default_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
3 身份验证插件使用情况
本文提供有关安装和使用身份验证插件的一般说明。通常,可插拔身份验证在服务器端和客户端使用一对相应的插件,因此您可以使用如下给定的身份验证方法:
- 如有必要,请安装包含适当插件的一个或多个插件库。在服务器主机上,安装包含服务器端插件的库,以便服务器可以使用它来验证客户端连接。同样,在每个客户端主机上,安装包含客户端插件的库,供客户端程序使用。不需要安装内置的身份验证插件。
- 对于您创建的每个MySQL帐户,指定用于身份验证的适当服务器端插件。如果帐户要使用默认的身份验证插件,则帐户创建语句无需显式指定该插件。服务器分配默认的身份验证插件,如“默认身份验证插件”中所述确定。
- 当客户端连接时,服务器端插件告诉客户端程序要使用哪个客户端插件进行身份验证。
在帐户使用服务器和客户端程序默认的身份验证方法的情况下,服务器不需要与客户端通信要使用哪个客户端插件,并且可以避免客户端/服务器协商中的往返。
对于标准的MySQL客户端,如MySQL和mysqladmin,可以在命令行上指定--default auth=plugin_name选项,作为程序可以使用的客户端插件的提示,尽管如果与用户帐户关联的服务器端插件需要不同的客户端插件,则服务器会覆盖此选项。
如果客户端程序找不到客户端插件库文件,请指定--plugin-dir=dir_name选项来指示插件库目录位置。