存储程序(过程、函数、触发器和事件)和视图是在使用之前定义的,并在执行时在确定其权限的安全上下文中执行。存储对象的权限由其 DEFINER
属性和 SQL SECURITY
特征控制。
存储对象定义可以包括一个 DEFINER
属性,指定一个 MySQL 帐户。如果定义省略了 DEFINER
属性,默认对象定义者是创建它的用户。
以下规则确定可以指定为存储对象 DEFINER
属性的帐户:
-
如果您拥有
SET_ANY_DEFINER
权限(或已弃用的SET_USER_ID
或SUPER
权限),您可以指定任何帐户作为DEFINER
属性。如果帐户不存在,将生成警告。此外,要将存储对象DEFINER
属性设置为拥有SYSTEM_USER
权限的帐户,您必须拥有SYSTEM_USER
权限。 -
否则,唯一允许的帐户是您自己的,指定为文字或
CURRENT_USER
或CURRENT_USER()
。您不能将定义者设置为任何其他帐户。
创建具有不存在的 DEFINER
帐户的存储对象将创建孤立对象,这可能会产生负面后果;见 孤立存储对象。
对于存储例程(过程和函数)和视图,对象定义可以包括一个 SQL SECURITY
特征,具有 DEFINER
或 INVOKER
值,以指定对象是在定义者或调用者上下文中执行的。如果定义省略了 SQL SECURITY
特征,默认为定义者上下文。
触发器和事件没有 SQL SECURITY
特征,始终在定义者上下文中执行。服务器自动调用这些对象,如有必要,因此没有调用用户。
定义者和调用者安全上下文不同如下:
-
在定义者安全上下文中执行的存储对象,以其
DEFINER
属性指定的帐户的权限执行。这些权限可能与调用用户的权限完全不同。在对象执行期间,调用用户的权限将被忽略,只有DEFINER
帐户的权限才重要。如果DEFINER
帐户权限很少,对象的操作能力将相应地受到限制。如果DEFINER
帐户是高特权的(例如管理员帐户),对象可以执行强大的操作 无论谁调用它。 -
存储的例程或视图在调用者安全上下文中执行时,只能执行调用者拥有权限的操作。
DEFINER
属性对对象执行没有影响。
考虑以下存储过程,它以 SQL SECURITY DEFINER
声明,以在定义者安全上下文中执行:
CREATE DEFINER = 'admin'@'localhost' PROCEDURE p1()
SQL SECURITY DEFINER
BEGIN
UPDATE t1 SET counter = counter + 1;
END;
任何拥有 p1
的 EXECUTE
权限的用户都可以使用 CALL
语句调用它。然而,当 p1
执行时,它以定义者安全上下文执行,因此以 'admin'@'localhost'
帐户的权限执行,该帐户是其 DEFINER
属性指定的帐户。该帐户必须拥有 p1
的 EXECUTE
权限,以及在对象体中引用的表 t1
的 UPDATE
权限。否则,过程将失败。
现在考虑这个存储过程,它与 p1
相同,除了其 SQL SECURITY
特征是 INVOKER
:
CREATE DEFINER = 'admin'@'localhost' PROCEDURE p2()
SQL SECURITY INVOKER
BEGIN
UPDATE t1 SET counter = counter + 1;
END;
与 p1
不同,p2
在调用者安全上下文中执行,因此以调用用户的权限执行,而不管 DEFINER
属性的值如何。p2
将失败,如果调用者缺少 EXECUTE
权限或表 t1
的 UPDATE
权限。
孤立存储对象是指其 DEFINER
属性命名的帐户不存在的对象:
-
可以通过在对象创建时指定不存在的
DEFINER
帐户来创建孤立存储对象。 -
现有的存储对象可以通过执行
DROP USER
语句删除对象DEFINER
帐户,或者执行RENAME USER
语句重命名对象DEFINER
帐户而变成孤立的。
孤立存储对象可能会出现以下问题:
-
因为
DEFINER
帐户不存在,该对象可能不会按预期工作,如果它在定义者安全上下文中执行:-
对于存储过程,如果
SQL SECURITY
值为DEFINER
但定义者帐户不存在,则在过程执行时将出现错误。 -
对于触发器,不建议在帐户不存在时激活触发器。否则,关于权限检查的行为将是未定义的。
-
对于事件,如果帐户不存在,则在事件执行时将出现错误。
-
对于视图,如果
SQL SECURITY
值为DEFINER
但定义者帐户不存在,则在引用视图时将出现错误。
-
-
该对象可能会带来安全风险,如果不存在的
DEFINER
帐户后来被重新创建用于与对象无关的目的。在这种情况下,帐户 “采用” 了该对象,并且如果拥有适当的权限,可以执行该对象,即使这不是预期的结果。
服务器实施了以下帐户管理安全检查,以防止操作可能使存储对象变成孤立的或使孤立的存储对象被采用:
-
DROP USER
语句将失败,如果要删除的帐户是任何存储对象的DEFINER
属性。(也就是说,语句将失败,如果删除帐户将使存储对象变成孤立的。) -
RENAME USER
语句将失败,如果要重命名的帐户是任何存储对象的DEFINER
属性。(也就是说,语句将失败,如果重命名帐户将使存储对象变成孤立的。) -
CREATE USER
如果要创建的账户名称与任何存储对象的DEFINER
属性相同,将失败。(也就是说,该语句将失败,因为创建账户将导致账户继承当前孤立的存储对象。)
在某些情况下,可能需要执行这些账户管理语句,即使它们通常会失败。在这种情况下,如果用户拥有 ALLOW_NONEXISTENT_DEFINER
权限(或已弃用的 SET_USER_ID
权限),该权限将覆盖孤立对象安全检查,并且语句将以警告而不是错误失败。
要获取 MySQL 安装中的账户信息,查询 INFORMATION_SCHEMA
。
该查询标识哪些 INFORMATION_SCHEMA
表描述了具有 DEFINER
属性的对象:
mysql> SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = 'DEFINER';
+--------------------+------------+
| TABLE_SCHEMA | TABLE_NAME |
+--------------------+------------+
| information_schema | EVENTS |
| information_schema | ROUTINES |
| information_schema | TRIGGERS |
| information_schema | VIEWS |
+--------------------+------------+
结果将告诉您查询哪些表以发现哪些存储对象 DEFINER
值存在,并且哪些对象具有特定的 DEFINER
值:
-
要标识哪些
DEFINER
值存在于每个表中,请使用这些查询:SELECT DISTINCT DEFINER FROM INFORMATION_SCHEMA.EVENTS; SELECT DISTINCT DEFINER FROM INFORMATION_SCHEMA.ROUTINES; SELECT DISTINCT DEFINER FROM INFORMATION_SCHEMA.TRIGGERS; SELECT DISTINCT DEFINER FROM INFORMATION_SCHEMA.VIEWS;
查询结果对于任何显示的账户非常重要:
-
如果账户存在,删除或重命名它将导致存储对象变为孤立的。如果您计划删除或重命名账户,请首先删除其关联的存储对象或重新定义它们以具有不同的定义者。
-
如果账户不存在,创建它将导致它继承当前孤立的存储对象。如果您计划创建账户,请考虑是否将孤立对象与之关联。如果不是,请重新定义它们以具有不同的定义者。
要重新定义对象以具有不同的定义者,可以使用
ALTER EVENT
或ALTER VIEW
直接修改事件和视图的DEFINER
账户。对于存储过程和函数以及触发器,必须删除对象然后重新创建以分配不同的DEFINER
账户 -
-
要标识哪些对象具有给定的
DEFINER
账户,请使用这些查询, substituting 帐户名称和主机名称:SELECT EVENT_SCHEMA, EVENT_NAME FROM INFORMATION_SCHEMA.EVENTS WHERE DEFINER = 'user_name@host_name'; SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE FROM INFORMATION_SCHEMA.ROUTINES WHERE DEFINER = 'user_name@host_name'; SELECT TRIGGER_SCHEMA, TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS WHERE DEFINER = 'user_name@host_name'; SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE DEFINER = 'user_name@host_name';
对于
ROUTINES
表,该查询包括ROUTINE_TYPE
列,以便输出行区分存储过程或存储函数的DEFINER
。如果您搜索的账户不存在,那么这些查询显示的对象都是孤立对象。
为了最小化存储对象创建和使用的风险,请遵循以下指南:
-
不要创建孤立存储对象,即对象的
DEFINER
属性命名了不存在的账户。不要通过删除或重命名账户使存储对象变为孤立的。 -
对于存储例程或视图,请在对象定义中使用
SQL SECURITY INVOKER
,以便它只能由具有适当权限的用户使用。 -
如果您使用具有
SET_ANY_DEFINER
(或已弃用的SET_USER_ID
或SUPER
权限)创建定义者上下文存储对象,请指定一个明确的DEFINER
属性,该属性命名了仅具有对象执行操作所需权限的账户。仅在绝对必要时指定高度特权的DEFINER
账户。 -
管理员可以通过不授予用户
SET_ANY_DEFINER
(或已弃用的SET_USER_ID
或SUPER
权限)来防止用户创建具有高度特权DEFINER
账户的存储对象。 -
定义器上下文对象应该以可能访问无权访问的数据为前提编写。在某些情况下,您可以通过不授予未经授权的用户特定权限来防止对这些对象的引用:
然而,对触发器和事件没有这样的控制,因为它们总是在定义器上下文中执行。服务器会自动根据需要调用这些对象,而用户不会直接引用它们:
-
触发器由与其关联的表的访问激活,即使是普通用户的表访问。
-
事件由服务器根据计划执行。
在这两种情况下,如果
DEFINER
帐户具有高特权,则对象可能会执行敏感或危险的操作。这仍然是true,即使创建对象所需的权限从创建它的用户帐户中被撤销。管理员应该特别小心地授予用户对象创建权限。 -
-
默认情况下,当具有
SQL SECURITY DEFINER
特征的例程被执行时,MySQL Server不会为DEFINER
子句中命名的MySQL帐户设置任何活动角色,只设置默认角色。除非activate_all_roles_on_login
系统变量启用,在这种情况下,MySQL Server将设置DEFINER
用户的所有角色,包括强制角色。因此,默认情况下,不会检查通过角色授予的权限。当CREATE PROCEDURE
或CREATE FUNCTION
语句被发出时。对于存储程序,如果执行应该使用与默认角色不同的角色,则程序体可以执行SET ROLE
以激活所需的角色。这必须小心,因为角色授予的权限可以更改。