必须立刻回收file权限,否则sql注入可直接读取/etc/passwd或写入webshell;file权限允许mysql进程以mysql用户身份读写任意本地文件,load_file()不受secure_file_priv限制,预处理语句也无法防御。

必须立刻回收 FILE 权限,否则任何 SQL 注入都可能直接读取 /etc/passwd、/proc/self/environ 甚至 Web 应用配置文件。
FILE 权限为什么等于“开锁钥匙”
MySQL 的 FILE 权限不是用来导出报表的——它允许服务器进程以 MySQL 进程身份(通常是 mysql 用户)读写任意本地路径。一旦应用存在注入点,攻击者就能执行:SELECT LOAD_FILE('/etc/passwd') 或 SELECT ... INTO OUTFILE '/var/www/html/shell.php'。预处理语句对这类操作完全无效,因为 LOAD_FILE() 和 INTO OUTFILE 不接受参数化。
常见错误现象:
- 应用用了 PDO::prepare,但依然被读出
/etc/my.cnf -
secure_file_priv设为/tmp,却忘了账号仍有FILE权限,仍可读/tmp下任意文件 - 云数据库(如阿里云 RDS)显示
secure_file_priv为空,但只要账号有FILE,就可能绕过限制
如何确认并回收 FILE 权限
用高权限账号(如 root@localhost)登录后执行:
SHOW GRANTS FOR 'app_user'@'%';
如果输出中含 FILE ON *.*,立即回收:
-
REVOKE FILE ON *.* FROM 'app_user'@'%';(注意:ON 后必须是*.*,不能是mydb.*) FLUSH PRIVILEGES;- 验证是否生效:
SELECT LOAD_FILE('/etc/passwd');应返回NULL或报错ERROR 1290 (HY000)
别信“只给 SELECT 就安全”——只要带 FILE,SELECT 就能变成文件读取指令。
secure_file_priv 不是保险柜,只是抽屉锁
secure_file_priv 只限制 LOAD DATA INFILE 和 SELECT ... INTO OUTFILE 的路径,但不影响 LOAD_FILE()——后者只要 FILE 权限在,就能读任意路径(只要 MySQL 进程有读权限)。
检查当前设置:
SELECT @@secure_file_priv;
理想值应为具体目录(如 /var/lib/mysql-files),绝不能是空字符串或 /。但即使设了,也必须配合 REVOKE FILE 才真正安全。
启动时加 --secure-file-priv=/tmp 是辅助手段,不是替代方案。
业务真需要导入导出怎么办
不要给应用账号开 FILE。改用客户端侧方案:
- 用
LOAD DATA LOCAL INFILE(注意是LOCAL):文件由客户端读取、经网络传输,服务端不碰文件系统 - 确保服务端已禁用该功能:
local_infile=OFF(MySQL 默认关闭) - 客户端连接时显式启用:
mysqli_options($conn, MYSQLI_OPT_LOCAL_INFILE, true)或 PDO DSN 加;local_infile=1 - 导入操作由运维通过
mysql --local-infile=1 -e "LOAD DATA LOCAL..."手动执行,不走应用接口
真正的风险点从来不在语法怎么写,而在于——你是否默认信任了那个拥有 FILE 权限的账号能永远不被注入。它只要被注入一次,/etc/shadow 就已经躺在攻击者本地磁盘上了。











