当前位置: 首页 > news >正文

MySQL definer does not exist 问题分析

1. 问题表现

使用MySQL存储过程、函数、视图或触发器时,遇到报错

代码1  定义者不存在错误

The user specified as a definer ('someone'@'somewhere') does not exist

为简化表述,下文以存储过程指代存储过程、函数、视图和触发器等对象。

2. 解决方案

2.1. 方案1 创建用户

代码2  创建用户

CREATE USER 'someone'@'somewhere' IDENTIFIED BY 'password';

2.2. 方案1 修改存储过程定义者(需要ALTER ROUTINE和SET_USER_ID权限)

代码3  修改存储过程定义者

ALTER PROCEDURE db_name.proc_name DEFINER=`admin`@`localhost`;

2.3. 方案3 重建对象

  • 查看对象定义。

代码4  查看存储过程定义

SHOW CREATE PROCEDURE proc_name;
  • 备份。
  • 删除对象。
  • 重建对象。

3. 原因​

  • MySQL在删除用户时没有检查或删除关联对象​,导致孤儿对象产生。
  • 具有SET_USER_ID权限的用户可以将不存在的用户定义为DEFINER。

4. 原因分析

在MySQL数据库中,用户A建立的存储过程可以提供给用户B使用。用户A和B的权限可能不同,那么在执行存储过程时,使用哪个用户的权限呢?为了处理这个问题,MySQL引入了安全上下文概念。安全上下文记录了用户身份和权限信息。每个用户都有安全上下文,保存在系统表中,每个会话也有安全上下文。在建立存储过程时,可以选择执行时使用定义者或调用者的安全上下文。二者分别对应SQL SECURITY DEFINER和SQL SECURITY INVOKER选项。如果定义存储过程时没有明确指定,MySQL会使用默认选项SQL SECURITY DEFINER,用定义者安全上下文执行存储过程。对于这类存储过程,执行前首先会切换到定义者安全上下文,如果此时发现定义者不存在,MySQL就会报错。

为何会出现定义者丢失的情况呢?主要的原因是MySQL在删除用户时没有进行相关检查,导致孤儿对象的产生。另一个原因是拥有SET_USER_ID权限的用户可以将存储过程的定义者设置为不存在的用户。

各主要数据库都存在定义者、调用者等类似概念。请参考附录[示例代码]部分。

代码5  mysql-8.0.13 切换安全上下文代码 sql/auth/sql_security_ctx.cc

/**Initialize this security context from the passed in credentialsand activate it in the current thread.@param       thd@param       definer_user@param       definer_host@param       db@param[out]  backup  Save a pointer to the current security contextin the thread. In case of success it points to thesaved old context, otherwise it points to NULL.@note The Security_context_factory should be used as a replacement to thisfunction at every opportunity.During execution of a statement, multiple security contexts maybe needed:- the security context of the authenticated user, used as thedefault security context for all top-level statements- in case of a view or a stored program, possibly the securitycontext of the definer of the routine, if the object isdefined with SQL SECURITY DEFINER option.The currently "active" security context is parameterized in THDmember security_ctx. By default, after a connection isestablished, this member points at the "main" security context- the credentials of the authenticated user.Later, if we would like to execute some sub-statement or a partof a statement under credentials of a different user, e.g.definer of a procedure, we authenticate this user in a localinstance of Security_context by means of this method (andultimately by means of acl_getroot), and make thelocal instance active in the thread by re-settingthd->m_security_ctx pointer.Note, that the life cycle and memory management of the "main" andtemporary security contexts are different.For the main security context, the memory for user/host/ip isallocated on system heap, and the THD class frees this memory inits destructor. The only case when contents of the main securitycontext may change during its life time is when someone issuedCHANGE USER command.Memory management of a "temporary" security context isresponsibility of the module that creates it.@retval true  there is no user with the given credentials. The errois reported in the thread.@retval false success
*/bool Security_context::change_security_context(THD *thd,const LEX_CSTRING &definer_user,const LEX_CSTRING &definer_host,LEX_STRING *db,Security_context **backup) {bool needs_change;DBUG_ENTER("Security_context::change_security_context");DBUG_ASSERT(definer_user.str && definer_host.str);*backup = NULL;needs_change =(strcmp(definer_user.str, thd->security_context()->priv_user().str) ||my_strcasecmp(system_charset_info, definer_host.str,thd->security_context()->priv_host().str));if (needs_change) {if (acl_getroot(thd, this, const_cast<char *>(definer_user.str),const_cast<char *>(definer_host.str),const_cast<char *>(definer_host.str), db->str)) {my_error(ER_NO_SUCH_USER, MYF(0), definer_user.str, definer_host.str);DBUG_RETURN(true);}*backup = thd->security_context();thd->set_security_context(this);}DBUG_RETURN(false);
}

5. 预防措施​

  • 删除或重命名用户前检查关联对象。

6. 附录

6.1. SQL SECURITY DEFINER使用参考

应用场景

  1. 允许低权限用户执行需要高权限的操作。
  2. 限制用户只能通过预定义的接口访问数据。
  3. 所有操作都以DEFINER身份记录,便于追踪。

安全注意事项

  1. DEFINER账户应有最小必要权限。
  2. DEFINER权限过高可能导致权限提升攻击。
  3. 通常应使用特定应用账户(而非root)作为DEFINER。

管理建议

  1. 明确指定DEFINER用户。
  2. 定期审查DEFINER账户权限。
  3. 对敏感操作使用DEFINER时要格外谨慎。
  4. 考虑结合使用存储过程和视图进行细粒度访问控制。

6.2. 示例代码

代码6  openGauss

CREATE OR REPLACE PROCEDURE proc_name()
AUTHID CURRENT_USER  -- AUTHID DEFINER/SECURITY DEFINER/SECURITY INVOKER
AS $$
BEGIN-- 过程体
END;
$$;

代码7  postgreSQL

CREATE FUNCTION func_name() 
RETURNS void
SECURITY DEFINER  -- 或 SECURITY INVOKER
AS $$
BEGIN-- 函数体
END;
$$ LANGUAGE plpgsql;

代码8  SQL Server

CREATE PROCEDURE proc_name
WITH EXECUTE AS 'user_name'  -- EXECUTE AS CALLER/OWNER/SELF/'user_name'
AS
BEGIN-- 过程体
END;

代码9  SQL SECURITY INVOKER示例

CREATE PROCEDURE procedure_name()
SQL SECURITY INVOKER
BEGIN-- 过程代码
END;

代码10  SQL SECURITY INVOKER示例

CREATE PROCEDURE procedure_name()
SQL SECURITY DEFINER
BEGIN-- 存储过程代码
END;

代码11  查找按定义者查找存储过程

SELECT routine_schema, routine_name, routine_type 
FROM information_schema.routines 
WHERE definer = 'root@localhost';

代码12  检查DEFINER不存在的对象

SELECT object_type, object_schema, object_name, definer
FROM (SELECT 'PROCEDURE' AS object_type, routine_schema, routine_name, definerFROM information_schema.routinesUNIONSELECT 'VIEW', table_schema, table_name, definerFROM information_schema.viewsUNIONSELECT 'TRIGGER', trigger_schema, trigger_name, definerFROM information_schema.triggers
) all_objects
WHERE definer NOT IN (SELECT CONCAT(user,'@',host) FROM mysql.user);

7. 参考资料

https://dev.mysql.com/doc/refman/8.0/en/stored-objects-security.html#stored-objects-security-examples

http://www.dtcms.com/a/320496.html

相关文章:

  • 【Qt开发】常用控件(二) -> enabled
  • Prometheus监控平台部署
  • java分布式定时任务
  • 使用 Setup Project 打包
  • 在嵌入式操作系统中,TerminateTask函数
  • Java 使用 SSHJ 执行 SSH 命令和 SFTP 文件上传和下载
  • 亚麻云之平步轻云
  • GDB中thread apply all命令使用指南
  • 正确使用 JetBrains
  • Upscayl – 免费开源的 AI 图像放大工具,跨平台使用
  • FastAPI的BackgroundTasks如何玩转生产者-消费者模式?
  • HTML 与 CSS:从 “认识标签” 到 “美化页面” 的入门指南
  • application.properties配置文件详解
  • 【Android笔记】Android 自定义 TextView 实现垂直渐变字体颜色(支持 XML 配置)
  • Linux 内存管理之page folios
  • Kiro智能化代码重构实战:从遗留系统到现代架构的AI驱动转型
  • STM32HAL 快速入门(二):用 CubeMX 配置点灯程序 —— 从工程生成到 LED 闪烁
  • ELK+Redis+Nginx多节点部署实战:从日志收集到可视化分析
  • 大语言模型提示工程与应用
  • 破界之光:DeepSeek 如何重构AI搜索引擎的文明坐标 || #AIcoding·八月创作之星挑战赛#
  • 分治-快排-215.数组中的第k个最大元素-力扣(LeetCode)
  • GPT OSS 双模型上线,百度百舸全面支持快速部署
  • MCP实现:.Net实现MCP服务端 + Ollama ,MCP服务端工具调用
  • 构建响应式在线客服聊天系统的前端实践 Vue3+ElementUI + CSS3
  • RAG实现:.Net + Ollama + Qdrant 实现文本向量化,实现简单RAG
  • 【优选算法】BFS解决拓扑排序
  • Spring Boot + ECharts 极简整合指南:从零实现动态数据可视化大屏
  • Java Stream API 详解与实战案例
  • 广东省省考备考(第七十天8.8)——言语、判断推理(强化训练)
  • Ubuntu 22.04 安装 Docker 完整指南