load data local 会引发任意文件读取,因客户端本地读取文件发送服务端,服务端不校验路径与权限;需服务端(--local-infile=0)与客户端(如pymysql设local_infile=false)双侧禁用,并验证select @@local_infile为0。

为什么 LOAD DATA LOCAL 会引发任意文件读取?
因为客户端(比如 MySQL CLI、Navicat、甚至某些 Web 应用的数据库连接库)在执行 LOAD DATA LOCAL INFILE 时,会由客户端本地读取文件并发送给服务端——服务端不校验路径、不检查权限,只管接收。攻击者一旦能控制 SQL 语句(如注入点),就能让服务端“诱导”客户端读取 /etc/passwd、~/.my.cnf 等敏感文件,再通过报错或盲注方式回显。
禁用方法:服务端 + 客户端双侧关死
只改服务端配置是不够的——很多客户端默认开启 LOCAL INFILE,且会主动尝试协商该能力。必须两端同步限制:
- 服务端:启动时加
--local-infile=0,或在my.cnf的[mysqld]段写local_infile = OFF,然后重启 mysqld - 客户端:连接时显式禁用,例如 MySQL CLI 加
--local-infile=0;Python 的pymysql初始化时传local_infile=False;PHP 的mysqli要调用mysqli_options($conn, MYSQLI_OPT_LOCAL_INFILE, false) - 验证是否生效:连上后执行
SELECT @@local_infile;,返回0才算成功;再试LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE t1;,应报错ERROR 1148 (42000): The used command is not allowed with this MySQL configuration
常见绕过场景和对应堵点
有些应用看似禁用了,实则留了后门:
- 用
mysql --defaults-file=/tmp/evil.cnf启动 CLI:攻击者可伪造配置文件重开local_infile→ 解决办法:生产环境禁止非白名单用户执行mysql命令,或统一用 wrapper 脚本过滤参数 - 使用旧版 Connector/J(&allowLoadLocalInfile=false
- MySQL 8.0+ 默认关闭,但若启用了
skip-grant-tables或用--init-file启动,可能被绕过 → 禁用这些调试模式,不给降级执行空间
兼容性与性能影响几乎为零
LOAD DATA LOCAL 本身不是核心功能,绝大多数业务逻辑走的是 INSERT 或应用层批量写入。禁用后:
- 所有标准 INSERT、REPLACE、UPDATE 不受影响
- 服务端
LOAD DATA INFILE(无 LOCAL)仍可用,只是要求文件必须在数据库服务器本地且有 FILE 权限 - 唯一真实影响:运维手动导入 CSV 时得先
scp到 DB 机器,再用不带 LOCAL 的语句 —— 这反而是更安全的操作路径
真正容易被忽略的是:Docker 镜像、K8s initContainer、RDS 参数组这些托管环境里,local_infile 可能被默认打开,或者配置未生效却没验证。上线前务必连上去跑一遍 SELECT @@local_infile;。











