禁用 file 权限是提权防护第一道门槛,因其允许通过 load data infile 或 select ... into outfile 读写服务器文件,成为攻击者提权最廉价跳板;需显式 revoke 并检查现有账号。

为什么禁用 FILE 权限是提权防护的第一道门槛
MySQL 的 FILE 权限允许用户通过 LOAD DATA INFILE 或 SELECT ... INTO OUTFILE 读写服务器文件系统,攻击者一旦拿到低权限账号,常借此读取 /etc/passwd、my.cnf,甚至写入 Web 目录下的 PHP 木马。它不是“可选功能”,而是提权链上最廉价的跳板。
实操建议:
- 新建账号时显式拒绝:
CREATE USER 'app'@'%' IDENTIFIED BY 'pwd'; REVOKE FILE ON *.* FROM 'app'@'%'; - 检查现有账号:
SELECT User, Host, File_priv FROM mysql.user WHERE File_priv = 'Y';,逐个REVOKE FILE - 注意:即使账号没被显式授权
FILE,若拥有GRANT OPTION且能连到mysql库,仍可能间接获得——所以也要限制GRANT OPTION
UDF 动态库加载为何比 FILE 更危险
CREATE FUNCTION ... SONAME 允许加载自定义 C 函数,等价于在 MySQL 进程内执行任意本地代码。只要攻击者能写入临时目录(如 /tmp)并拥有 INSERT + CREATE ROUTINE 权限,就能绕过所有 SQL 层面的过滤直抵系统层。
实操建议:
- 启动 mysqld 时加参数:
--secure-file-priv=/dev/null --plugin-dir=/nonexistent(后者让INSTALL PLUGIN失败,间接阻断 UDF 注册) - 更彻底的做法:编译 MySQL 时用
-DWITHOUT_UDF=ON,或运行时在配置中设secure_file_priv = /dev/null并确保plugin_dir指向空目录 - 检查是否已存在恶意 UDF:
SELECT name, dl FROM mysql.func;,发现异常dl路径(如指向/tmp/)立即DROP FUNCTION
权限最小化落地时最容易被忽略的三个点
很多人 revoke 了 FILE、关了 UDF,却在其他环节留了后门。真正卡住提权路径,得盯住这三处。
-
PROCESS权限:允许执行SHOW PROCESSLIST,可窥探其他连接的完整 SQL(含明文密码),应仅授予 DBA 账号 -
SUPER权限:能动态修改全局变量(比如临时开local_infile=ON),非必要不给 - 数据库名通配符滥用:
GRANT SELECT ON `app_%`.* TO ...看似安全,但若攻击者能创建数据库app_hack,就自动获得该库全部权限——改用精确库名授权
验证防护是否生效的关键检查项
别只信配置文件和命令行参数,MySQL 启动后必须亲手试。
- 以目标账号登录,执行:
SELECT LOAD_FILE('/etc/passwd');→ 应返回NULL且无错误;若报错ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option...,说明生效 - 尝试写文件:
SELECT 'test' INTO OUTFILE '/tmp/x';→ 必须失败,且错误码应为ERROR 1045(权限拒绝)而非ERROR 1290(路径限制) - 查 UDF 状态:
SHOW VARIABLES LIKE 'plugin_dir';返回路径若可写、且SELECT * FROM mysql.func;非空,就得立刻清空并重启
复杂点在于:有些云数据库(如阿里云 RDS)默认禁用 FILE 和 UDF,但允许你开白名单;而私有部署的 Percona Server 可能默认启用 local_infile,哪怕 secure_file_priv 设了也照样能读。得按实际版本+部署形态分别验证。










