MySQL 不提供 SYS_EXEC 函数,它是第三方插件 lib_mysqludf_sys 注入的危险扩展,绕过权限模型以进程用户身份执行系统命令,生产环境启用等于赋予数据库 root 权限。

为什么 MySQL 的 SYS_EXEC 默认不可用且极其危险
MySQL 本身不提供 SYS_EXEC 函数——这是第三方插件(如 lib_mysqludf_sys)注入的非标准扩展。它绕过 SQL 权限模型,以 MySQL 进程用户身份直接调用系统 shell,意味着一旦被注入或误用,攻击者可读写任意文件、启停服务、甚至反弹 shell。生产环境开启等于给数据库进程配了一把服务器 root 钥匙。
- 该函数不属于 MySQL 官方功能,不同版本/发行版(如 Percona、MariaDB)默认均不包含
- 即使手动安装,MySQL 8.0+ 默认禁用自定义 UDF(
secure_file_priv和plugin_dir权限双重限制) - 常见错误现象:
FUNCTION SYS_EXEC does not exist或Access denied for function 'sys_exec',往往不是配置问题,而是权限或插件根本未加载
如何确认是否真有 SYS_EXEC 并定位风险源头
别猜,直接查。只要没明确装过 lib_mysqludf_sys,就大概率没有;但有些运维脚本或旧迁移包会静默安装。
- 执行
SELECT * FROM mysql.func WHERE name = 'sys_exec';—— 若返回一行,说明函数已注册 - 检查插件路径:
SHOW VARIABLES LIKE 'plugin_dir';,然后在对应目录下找lib_mysqludf_sys.so(Linux)或.dll(Windows) - 查看当前用户权限:
SHOW GRANTS FOR CURRENT_USER;,重点看是否含FILE或EXECUTE(后者对 UDF 是必需的,但极少见) - 注意:即使函数存在,若 MySQL 启动时加了
--skip-secure-file-priv或secure_file_priv为空,风险会指数级放大
禁用或清除 SYS_EXEC 的实际操作步骤
最稳妥的方式不是“禁用”,而是彻底卸载。残留函数定义 + 缺失 SO 文件会导致报错,但更糟的是给人“已处理”的错觉。
- 先删除函数:
DROP FUNCTION IF EXISTS sys_exec;(必须用root或具有DELETE权限的账号) - 再删物理文件:到
plugin_dir路径下手动移除lib_mysqludf_sys.so(注意备份) - 重启 MySQL 服务(仅删函数不重启,仍可能被缓存调用)
- 验证是否清除:
SELECT sys_exec('id');应报错FUNCTION sys_exec does not exist,而非权限拒绝
替代方案:哪些场景真需要执行外部命令?该怎么安全做
99% 的所谓“需要调用 shell”需求,其实可以用更可控的方式达成:
- 日志归档?用
mysqldump+cron外部调度,而不是在存储过程中调sys_exec('tar ...') - 调用 HTTP 接口?用 MySQL 8.0+ 的
HTTP_CLIENT插件(需显式启用),或更推荐由应用层发起 - 触发通知?写入一张
notification_queue表,由独立守护进程轮询并执行 curl / sendmail - 性能影响:UDF 调用是同步阻塞的,一个慢 shell 命令会让整个连接卡死,而异步队列表方式可限流、重试、记录失败
真正难处理的是遗留系统里已经散落各处的 SYS_EXEC 调用。它们通常藏在视图定义、事件调度器或冷门存储过程中,靠人工 grep 很难扫全——得用 mysqldump --no-data --routines 导出所有例程再全局搜索 sys_exec。










