授予 file 权限等于开放服务器文件系统读写入口,允许用户通过 into outfile 和 load data infile 读写 mysql 进程可访问的任意本地文件,且仅受 secure_file_priv 路径白名单限制。
授予 file 权限等于开放服务器文件系统读写入口
mysql 的 file 权限不是“导出数据用的权限”,而是“让 mysql 进程以自身身份去读写服务器本地任意可访问路径”的能力。一旦用户拥有它,就能通过 select ... into outfile 写入 web 目录、通过 load data infile 读取 /etc/passwd、甚至配合 udf 或日志功能写入动态库。这不是配置疏漏,是直接绕过应用层的身份边界。
- 只要 MySQL 服务进程有权限(比如跑在
mysql用户下),FILE权限持有者就能读写该用户能碰的所有文件 -
secure_file_priv是唯一有效限制手段,但它只控制路径白名单,不校验内容或目标用途 - 即使禁用
local_infile,LOAD DATA INFILE仍可从服务端路径加载——FILE权限管的是服务端文件操作,和客户端是否启用无关
用 secure_file_priv 锁死可操作目录比授予权限更关键
授予权限前必须确认 secure_file_priv 已设为非空且受限路径,否则等于把整台机器的文件系统钥匙交出去。这个变量在 MySQL 启动时读取,运行中无法动态修改,且设为空值('')或 NULL 就等同于完全放开。
- 检查当前值:
SELECT @@secure_file_priv;—— 返回NULL或空字符串必须立刻处理 - 正确做法是在
my.cnf的[mysqld]段落添加:secure_file_priv = /var/lib/mysql-files/ - 该路径需由
mysql用户可读写,但不能是 Web 根目录、/tmp、用户主目录等高危位置 - 重启 MySQL 后,所有
INTO OUTFILE和LOAD DATA INFILE都只能在这个目录内进行
GRANT FILE ON *.* TO 'user'@'%' 的真实影响远超预期
这条语句不是“给某个用户开个导入导出小口子”,而是赋予其跨库、跨表、无视 SQL_MODE 的底层文件操作自由。它不区分 DML 或 DDL,也不校验语句意图——哪怕只是想导出一张表,背后执行的仍是 MySQL 进程调用 fopen() 写磁盘。
-
FILE是全局权限,无法按数据库、表、列做粒度控制 - 与
SELECT权限叠加后,攻击者可用SELECT load_file('/etc/shadow')直接读敏感文件(前提是secure_file_priv允许) - 某些云厂商 RDS 默认禁用
FILE权限且不允许修改secure_file_priv,强行 GRANT 会报错ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation
替代方案:用应用层中转 + 临时表规避 FILE 权限需求
绝大多数业务场景根本不需要 FILE 权限。导出 CSV?让应用连上数据库查完自己拼;导入 Excel?先解析成数组,再用批量 INSERT 或 REPLACE INTO ... VALUES 写入。只有极少数离线 ETL 或 DBA 手动维护才真需要它。
- 对 Web 应用,用
mysqldump --user=app --no-create-info --skip-extended-insert db table > out.sql在服务端生成再下载,比开放FILE安全得多 - 如必须用
LOAD DATA INFILE加速大批量导入,可改用LOAD DATA LOCAL INFILE(客户端文件上传),前提是 MySQL 客户端启用了local_infile=ON,且服务端未禁用该特性(local_infile=OFF是默认安全态) - 运维脚本若依赖
INTO OUTFILE,建议改用SELECT ... INTO DUMPFILE(仅限单行二进制输出)或直接重定向mysql -e "SELECT ..." > file.csv
真正麻烦的从来不是怎么配,而是配完没人记得它能让 MySQL 进程变成你的文件系统代理。删掉 FILE 权限不难,难的是下次谁在自动化脚本里又悄悄加上了。










