一、概述
  • 在定义视图时,需要设置一条用于以后调用的SELECT语句。在定义存储程序时,也有相同的操作,即需要定义一个以后执行的对象。这里的“以后执行”预示着实际执行这些对象的用户并不一定就是当初创建它们的那个用户,而这会引出一个很重要的问题:服务器在执行时应该使用什么样的安全上下文来检查访问权限呢?也就是说,应该应用哪个账户的权限呢?
  • 默认情况下,服务器会使用定义该对象的那个用户的账户。假设我定义了一个存储过程p(),用于访问我的表。如果我把p()的EXECUTE权限授予你,那么你便可以使用CALL p()来调用该过程,进而访问到我的表,因为它是以我的权限来运行的。
  • 这种类型的安全上下文有利有弊:
    • 有利的一面是:精心编写的存储程序可以把表开放给那些无法直接访问它们的用户,并可对访问进行控制。
    • 有弊的一面是:如果某个用户创建了某个可以访问到敏感数据的存储程序,但却忘记了其他可以调用该对象的人也会访问到那些敏感数据,则会造成安全漏洞。
二、DEFINER
CREATE DEFINER = 'sampdb'@'localhost' PROCEDURE count_student()
 SELECT COUNT(*) FROM student;
  • 在定义存储程序或视图时,显式指定定义者的方法是,在该对象的CREATE语句里包含一个DEFINER=account子句。这样,这里列出的账户就能跟定义者完全一样,从而达到在执行时检査访问权限的目的。
  • 在DEFINER子句里,定义者的具体值可以是格式为'user_name'@'host_name'的账户名,该格式与账户管理语句(如CREATE USER)里所用的格式一样。更多相关信息请参考javascript:void(0)
  • 对于这种格式,其中的user_name和host_name必不可少。
  • 此外,那个具体值还可以是CURRENT_USER或CURRENT_USER()。它表明定义者就是执行这条CREATE语句的那个用户的胀户。
  • 如果没有给出DEFINER子句,则默认使用同一个账户
  • SUPER权限:
    • 如果你拥有SUPER权限,则可以使用任何一种语法正确的账户名来作为DEFINER值。当账户不存在时,会出现一条警告消息。
    • 如果你没有SUPER权限,则只能把定义者设置为你自己的账户实现限执。既可以使用完整的账户名,也可以使用CURRENT_USER。
三、SQL SECURITY
  • 对于视图和存储例程(包括存储函数和存储过程),还可以使用SQL SECURITY特性,用于对执行时访问检査的附加控制
  • SQL SECURITY特性的允许值为DEFINER(以定义者的权行)或INVOKER(以对象调用者的权限执行)。
  • 适合使用SQL SECURITY INVOKER的场合是:
    • 只想以调用者所拥的权限来执行视图或存储程序。
    • 例如,下面这个视图将访问mysql数据库里的某个表,但是是以调用者的权限来运行。这样一来,如果调用者本人无权访问mysql.user表,那么即使他访问这个视图,也无法突破权限限制。
CREATE SQL SECURITY INVOKER VIEW v 
  AS SELECT CONCAT(User,'@', Host) AS Account, Password FROM mysql.user;
  • 服务器会自动调用触发器和事件,所以这里的“调用者”概念对它们并不适用。因此,它们没有SQL SECURITY特性,总是以定义者的权限来执行。
  • 如果某个视图或存储程序以定义者的权限来运行,但那个定义者账户并不存在,那么会出现一个错误。
  • 在视图或存储程序里,CURRENT_USER()函数会默认返回与该对象的DEFINER属性相对应的账户
  • 对于视图和定义时带有SQL SECURITY INVOKER特性的存储例程,CURRENT_USER()会返回调用用户的账户。