必须服务端和客户端双侧禁用local_infile才有效;服务端需在my.cnf[mysqld]段设local_infile=off并重启,客户端须显式禁用(如pymysql设local_infile=false),且均需验证select @@local_infile返回0。

服务端 local_infile=OFF 必须写进配置并重启
仅执行 SET GLOBAL local_infile = OFF 是临时的,MySQL 重启后恢复默认(8.0.22+ 默认为 OFF,但很多旧版本、Docker 镜像、RDS 参数组仍默认 ON)。真正生效必须改配置文件:
- 编辑
/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf - 在
[mysqld]段下添加local_infile = OFF(注意不是=0或=false,MySQL 只认ON/OFF) - 检查是否有其他段(如
[server]、[mysqld_safe])覆盖了该设置 - 执行
sudo systemctl restart mysql(或mariadb) - 连上去立刻验证:
SELECT @@local_infile;—— 返回0才算落地
客户端连接时必须显式禁用 local_infile
服务端关了,客户端开着,攻击面依然存在。不同客户端禁用方式不一致,漏掉任意一个都可能留后门:
- MySQL CLI:启动时加
--local-infile=0;或在~/.my.cnf的[client]段写local_infile = 0 - Python + PyMySQL:
pymysql.connect(..., local_infile=False)(注意:部分二进制包默认不编译 LOCAL INFILE 支持,不能依赖默认值) - Python + mysql-connector-python:
mysql.connector.connect(..., allow_local_infile=False) - PHP mysqli:
mysqli_options($conn, MYSQLI_OPT_LOCAL_INFILE, false),且必须在mysqli_real_connect()前调用 - Node.js mysql2:
new Pool({ ..., localInfile: false })
Docker、云数据库和自动化部署中最容易漏验证
这些环境常默认开启 local_infile,或配置写了但没生效也没检查:
- 阿里云 RDS、AWS RDS 等托管服务:不让你直接改
my.cnf,得通过参数组(Parameter Group)设置,但修改后不一定实时应用,控制台也不提示是否成功 - Docker 启动命令里如果带了
--local-infile,会覆盖配置文件,优先级更高 - K8s initContainer 中的 MySQL 容器,常复用基础镜像,默认未禁用
- 上线前必须手动连上去跑
SELECT @@local_infile;,不能只信文档、控制台开关或 CI/CD 日志里的“已重启”
为什么必须服务端和客户端双侧禁用
LOAD DATA LOCAL INFILE 不是服务端读自己磁盘,而是服务端“指挥”客户端读本地文件——比如 /etc/passwd 或 ~/.my.cnf。一旦有 SQL 注入点,攻击者就能让 PHP、Python 或 Navicat 把敏感文件发给服务端,再通过报错或盲注回显。
这个链路已被真实利用过,不是理论风险。而双侧禁用的本质是:
- 客户端禁用 → 请求根本发不出(不走网络)
- 服务端禁用 → 请求即使发出也会被拒收(返回错误)
- 任一端开着,就等于开了后门;只关一端,等于没关
最麻烦的是:有些客户端库(如老版本 PyMySQL)默认启用 local_infile,而你代码里没显式传参,就以为它是关的——这种隐式行为最容易被忽略。











