FILE权限本质是MySQL服务进程对服务器本地文件系统的读写权,而非仅限数据导出;它允许通过LOAD_FILE、INTO OUTFILE等操作读写任意可访问路径,配合secure_file_priv配置不当可导致敏感信息泄露或远程代码执行。

FILE权限不是“导出权限”,而是服务器文件系统操作权
很多人误以为GRANT FILE ON *.* TO 'user'@'%'只是让某个用户能用SELECT ... INTO OUTFILE导出数据,或用LOAD DATA INFILE导入数据。实际上,它赋予的是MySQL服务进程以自身身份(比如mysql用户)读写服务器本地任意路径的能力——只要该路径对MySQL进程可访问。
这意味着:用户无需登录操作系统,仅通过SQL语句就能:
-
SELECT LOAD_FILE('/etc/shadow')—— 直接读取系统敏感文件(前提是secure_file_priv未限制或设为空) -
SELECT ... INTO OUTFILE '/var/www/html/shell.php'—— 写入Web目录,触发远程代码执行 -
LOAD DATA INFILE '/usr/lib/mysql/plugin/udf.so'—— 配合UDF提权,获得更高权限
secure_file_priv才是唯一有效防线,但它极易被忽略或配错
secure_file_priv是MySQL启动时加载的只读变量,运行中无法修改。它的值决定INTO OUTFILE和LOAD DATA INFILE能操作的**唯一合法路径**。但很多环境里它被设为NULL或空字符串'',等同于完全放开。
检查当前配置:
SELECT @@secure_file_priv;
若返回NULL或'',必须立刻处理。正确做法是在my.cnf的[mysqld]段落中显式指定:
secure_file_priv = /var/lib/mysql-files/
注意以下几点:
- 该路径必须由
mysql用户可读写,但不能是/tmp、/var/www、/home等高危位置 - MySQL重启后才生效,且无法动态调整
- 即使禁用了
local_infile,LOAD DATA INFILE仍能从服务端路径加载——FILE权限管的是服务端行为,与客户端无关
FILE是全局权限,无法按库/表/列做粒度控制
不像SELECT或UPDATE可以限定到某张表,FILE权限一旦授予,就覆盖整个实例,且不校验SQL意图。哪怕用户只执行一条看似无害的导出语句,背后调用的仍是fopen()、fwrite()等底层系统调用。
叠加其他权限后风险急剧放大:
- 有
SELECT+FILE→ 可读任意文件(如/etc/passwd) - 有
INSERT+FILE→ 可向日志目录写恶意payload,触发日志功能执行 - 云厂商RDS通常默认禁用
FILE权限,且不允许修改secure_file_priv;强行GRANT会报错ERROR 1227 (42000)
替代方案比授予权限更安全也更可行
绝大多数业务场景根本不需要FILE权限。真正需要导出数据时,优先考虑应用层中转:
- 用应用连接数据库,查出结果后由应用写入本地文件或提供下载链接
- 用
mysqldump配合操作系统权限控制(如限制执行者为backup组) - 对临时需求,建专用临时表 +
SELECT ... INTO OUTFILE仅在secure_file_priv受限路径下执行,用完立即清理文件
最常被忽略的一点:授权前不检查secure_file_priv是否已锁定路径,等于直接交出服务器文件系统的钥匙——而这个动作本身,比任何SQL注入都更接近“root级失控”。


















