存储的程序(过程、函数、触发器和事件)和视图在使用前定义,在引用时在确定其权限的安全上下文中执行。适用于执行存储对象的权限由其控制定义者
属性和SQL安全
的特点。
存储对象定义可以包括定义者
指定MySQL帐户的属性。的定义定义者
属性时,默认对象定义者是创建它的用户。
下面的规则确定可以将哪些帐户指定为定义者
属性:
如果你有
SET_USER_ID
特权(或已弃用的特权超级
特权),则可以将任何帐户指定为定义者
属性。如果该帐户不存在,则会产生警告。此外,设置存储的对象定义者
属性的帐户SYSTEM_USER
特权,你必须拥有SYSTEM_USER
特权。否则,唯一允许的帐户是您自己的,指定为字面或
CURRENT_USER
或CURRENT_USER ()
.不能将定义器设置为任何其他帐户。
使用不存在的对象创建存储对象定义者
Account创建了一个孤立对象,这可能会产生负面后果;看到孤儿存储对象.
对于存储例程(过程和函数)和视图,对象定义可以包含SQL安全
值为的特征定义者
或调用程序
指定对象是在定义程序上下文中执行还是在调用程序上下文中执行。如果定义省略了SQL安全
特性,默认为定义上下文。
触发器和事件没有SQL安全
特性,并且总是在定义上下文中执行。服务器根据需要自动调用这些对象,因此没有调用用户。
定义器和调用器安全上下文的区别如下:
考虑以下存储过程,它是用SQL安全定义器
在定义器安全上下文中执行:
CREATE DEFINER = 'admin'@'localhost' PROCEDURE 1() SQL SECURITY DEFINER BEGIN UPDATE t1 SET counter =计数器+ 1;结束;
的任何用户执行
特权的p1
可以使用调用
声明。然而,当p1
执行时,它在定义器安全上下文中执行,因此使用的权限执行“admin”@“localhost”
,该帐户被命名为其定义者
属性。此帐户必须具有执行
特权的p1
以及更新
表的特权t1
在对象体中引用。否则,该过程将失败。
现在考虑这个存储过程,它与p1
除了它SQL安全
特点是调用程序
:
CREATE DEFINER = 'admin'@'localhost' PROCEDURE p2() SQL SECURITY INVOKER开始更新t1 SET计数器=计数器+ 1;结束;
不像p1
,p2
在调用方安全上下文中执行,因此具有调用方用户的特权,而不管定义者
属性值。p2
属性,则失败执行
特权的p2
或者是更新
表的特权t1
.
孤儿存储对象是指其定义者
属性命名一个不存在的帐户:
孤儿存储对象可能在以下方面存在问题:
因为
定义者
Account不存在,如果在定义器安全上下文中执行,对象可能无法正常工作:对于存储的例程,如果在例程执行时出现错误
SQL安全
值是定义者
但是定义者帐户不存在。对于触发器,在帐户实际存在之前发生触发器激活并不是一个好主意。否则,有关特权检查的行为是未定义的。
对于事件,如果帐户不存在,则在事件执行时发生错误。
类型引用视图时,将发生错误
SQL安全
值是定义者
但是定义者帐户不存在。
如果不存在,则该对象可能存在安全风险
定义者
帐户随后被重新创建,目的与对象无关。在本例中,是帐户”采用”对象和具有适当的特权,即使不打算这样做,也能够执行它。
从MySQL 8.0.22开始,服务器强加了额外的帐户管理安全检查,旨在防止(可能是无意中)导致存储对象成为孤儿的操作,或导致采用当前为孤儿的存储对象:
在某些情况下,可能有必要故意执行这些帐户管理报表,即使它们在其他情况下会失败。要实现这一点,如果用户具有SET_USER_ID
特权,该特权将覆盖孤立对象安全检查,语句成功并提示警告,而不是失败并提示错误。
要获得有关MySQL安装中用作存储对象定义器的帐户的信息,请查询INFORMATION_SCHEMA
.
这个查询确定了INFORMATION_SCHEMA
表描述具有属性的对象定义者
属性:
从信息schema中选择TABLE_SCHEMA, TABLE_NAME。column_name = ' definer '的列;+--------------------+------------+ | TABLE_SCHEMA | TABLE_NAME | +--------------------+------------+ | 日常事件information_schema | | | information_schema | | | information_schema触发| | | information_schema |视图 | +--------------------+------------+
结果告诉您要查询哪些表以发现哪个存储对象定义者
值存在和哪些对象有特定的定义者
值:
来识别
定义者
值存在于每个表中,使用这些查询:从information_schema.events中选择不同的定义器;从information_schema.routines中选择不同的定义器;从information_schema.triggers中选择不同的定义器;从information_schema.views中选择不同的定义器;
查询结果显示如下:
如果帐户存在,删除或重命名它会导致存储的对象成为孤立的。如果计划删除或重命名帐户,请考虑先删除其关联的存储对象或重新定义它们以使用不同的定义器。
如果该帐户不存在,创建它会导致它采用当前孤立的存储对象。如果您计划创建帐户,请考虑是否应该将孤立对象与该帐户关联。如果不是,则重新定义它们以使用不同的定义符。
要使用不同的定义器重新定义对象,可以使用
改变事件
或改变观点
若要直接修改定义者
事件和观点的叙述。对于存储过程和函数以及触发器,必须删除对象并重新创建它以分配不同的定义者
账户来识别哪些对象具有给定的
定义者
帐户,使用这些查询,将感兴趣的帐户替换为
:user_name
@host_name
从information_schema中选择event_schema, event_name。definer = '的事件user_name@host_name”;在information_schema中选择routine_schema, routine_name, routine_type。definer = '的例程user_name@host_name”;从information_schema中选择trigger_schema, trigger_name。definer = '的触发器user_name@host_name”;从information_schema中选择table_schema, table_name。definer = '的视图user_name@host_name”;
为
例程
表中,查询包括ROUTINE_TYPE
列,使输出行区分是否定义者
用于存储过程或存储函数。如果您正在搜索的帐户不存在,则这些查询所显示的任何对象都是孤儿对象。
为了最小化存储对象创建和使用的潜在风险,请遵循以下指导原则:
不要创建孤立的存储对象;的对象
定义者
属性命名一个不存在的帐户。删除或重命名由?命名的帐户,不要导致存储的对象成为孤儿定义者
任何现有对象的属性。对于存储的例程或视图,使用
SQL安全调用程序
在对象定义中,以便仅由具有适合该对象执行的操作的权限的用户使用。属性时创建定义上下文存储对象
SET_USER_ID
特权(或已弃用的特权超级
Privilege),指定一个显式的定义者
属性,该属性指定仅拥有该对象执行的操作所需权限的帐户。指定高度特权定义者
只有在绝对必要时才考虑。管理员可以阻止用户创建指定高特权的存储对象
定义者
通过不授予他们SET_USER_ID
特权(或已弃用的特权超级
特权)。在编写定义上下文对象时应牢记,它们可能能够访问调用用户没有权限的数据。在某些情况下,可以通过不授予未授权用户特定权限来防止对这些对象的引用:
但是,对于触发器和事件不存在这样的控制,因为它们总是在定义上下文中执行。服务器根据需要自动调用这些对象,用户不直接引用它们:
触发器是通过访问与它相关联的表来激活的,即使是没有特殊权限的用户访问普通表。
事件由服务器在预定的基础上执行。
在这两种情况下,如果
定义者
帐户具有高度特权,对象可能能够执行敏感或危险的操作。如果创建对象的用户帐户撤销了创建对象所需的特权,则仍然如此。管理员在授予用户对象创建权限时应该特别小心。