必须同时回收FILE权限并设secure_file_priv = NULL(8.0.17+)或''(旧版),缺一不可;仅关local_infile或仅设路径无法阻止LOAD DATA INFILE读取服务端文件,漏任一环均可能被绕过。

必须同时回收FILE权限并设secure_file_priv = NULL
只关local_infile或只设secure_file_priv路径,都不能阻止LOAD DATA INFILE读取服务端文件。真正起效的是两个动作缺一不可:REVOKE FILE ON *.* FROM 'user'@'host' + secure_file_priv = NULL(MySQL 8.0.17+)或secure_file_priv = ''(旧版)。漏掉任一环,攻击者仍可能通过已有账户或角色绕过限制。
-
FILE是全局权限,不能按库或表撤销,必须用ON *.*语法;主机名要和mysql.user表中完全一致,比如'user'@'10.0.1.%'不能简写为'user'@'%' - 执行
REVOKE后必须立刻运行FLUSH PRIVILEGES,否则权限缓存可能延迟生效 - 验证是否清干净:
SELECT User,Host,File_priv FROM mysql.user WHERE File_priv='Y'结果里不应出现目标用户 -
secure_file_priv = NULL必须写在[mysqld]段,重启mysqld才生效;设成/或留空等于开放整个磁盘,绝对禁止
为什么secure_file_priv设目录不如设NULL
把secure_file_priv设成/var/lib/mysql-files只是锁住一个读写目录,但只要用户还有FILE权限,就能从该目录读my.cnf、导出表结构,甚至写入恶意SQL文件。而secure_file_priv = NULL会让所有依赖文件I/O的操作——包括LOAD DATA INFILE、SELECT INTO OUTFILE、LOAD_FILE()——直接报错ERROR 1290,不给任何路径穿越机会。
- MySQL 8.0.17+支持
secure_file_priv = NULL,旧版本只能用secure_file_priv = '' - 设为空字符串
''在旧版MySQL中效果等同于NULL,但若没同步回收FILE权限,MySQL启动会失败 -
SELECT @@secure_file_priv返回NULL才算真正生效,返回路径字符串说明还在受限模式下
别忘了验证LOAD DATA INFILE是否真被拦截
配置改完不验证,等于没做。最可靠的验证方式不是看变量值,而是实测语句是否失败:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 用已授权用户登录,执行
LOAD DATA INFILE '/etc/passwd' INTO TABLE t(表t需存在) - 必须报
ERROR 1290或ERROR 1045(Access denied),而不是ERROR 29(文件不存在)或ERROR 13(权限不足) - 如果报
ERROR 29,说明secure_file_priv没生效或路径限制太松;如果报ERROR 1045,说明FILE权限已成功回收 - 顺手再试
SELECT LOAD_FILE('/etc/passwd'),也应报ERROR 1290
root账号常被忽略,但它默认有FILE权限
很多团队只处理业务账号,却忘了root在初始化时默认拥有FILE权限。一旦数据库被提权,这个后门就直接暴露。检查命令:SELECT User,Host,File_priv FROM mysql.user WHERE User='root'。如果结果中File_priv为Y,必须同样执行REVOKE FILE ON *.* FROM 'root'@'localhost'(注意主机名匹配),再FLUSH PRIVILEGES。
最容易被绕过的点:secure_file_priv = NULL 和 REVOKE FILE 必须成对出现。只设前者,其他账户或未审计的角色可能持有FILE;只做后者,secure_file_priv设得再严,也挡不住已有FILE权限用户的路径穿越尝试。

















