应用程序可以使用以下指南执行基于 SQL 的审计,将数据库活动与 MySQL 帐户关联起来。
MySQL 帐户对应于 mysql.user
系统表中的行。 当客户端成功连接时,服务器会对该表中的特定行进行身份验证。 此行中的 User
和 Host
列值唯一标识帐户,并对应于 SQL 语句中写入帐户名称的 '
格式。user_name
'@'host_name
'
用于对客户端进行身份验证的帐户决定了客户端具有的权限。 通常,可以调用 CURRENT_USER()
函数来确定客户端用户的帐户。 它的值是由该帐户的 user
表行的 User
和 Host
列构造的。
但是,在某些情况下,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
域中的任何主机连接。 如果 user2
从 remote.example.com
连接,则 USER()
和 CURRENT_USER()
返回不同的值:
mysql> SELECT USER(), CURRENT_USER();
+--------------------------+---------------------+
| USER() | CURRENT_USER() |
+--------------------------+---------------------+
| [email protected] | user2@%.example.com |
+--------------------------+---------------------+
如果某个应用程序必须调用 USER()
函数进行用户审计(例如,如果它从触发器内部进行审计),但又必须能够将 USER()
值与 user
表中的帐户相关联,则必须避免在 User
或 Host
列中包含通配符的帐户。具体来说,不允许 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 |
+----------------------------------------+