必须同时禁用服务端local_infile和客户端--local-infile,仅关一端无效;因客户端默认可能主动发起本地文件上传协商,服务端拒绝需双向配合,且托管环境存在配置覆盖、延迟生效等常见漏检风险。

必须同时禁用服务端 local_infile 和客户端 --local-infile,只关一端等于没关。
为什么只改 my.cnf 不够
MySQL 服务端的 local_infile 只控制“是否接受客户端发来的本地文件数据”,但不阻止客户端主动发起协商。很多客户端(如 mysql CLI、pymysql、旧版 Connector/J)默认开启 LOCAL 能力,只要服务端没明确拒绝,它们就会尝试上传 —— 这正是任意文件读取漏洞的入口。
-
SET GLOBAL local_infile = 0是运行时生效,但重启后丢失;写进[mysqld]段才持久 - 某些 Docker 镜像或云数据库(如阿里云 RDS、腾讯云 CDB)默认开启
local_infile,且参数组修改后需手动触发“应用配置”才生效 -
SHOW VARIABLES LIKE 'local_infile'返回OFF仅代表服务端配置,不代表连接实际被限制
客户端连接时必须显式关闭 local_infile
服务端关了,客户端仍可能因历史配置、默认行为或注入点绕过限制。关键不是“能不能用”,而是“连上来就根本没这个能力”。
- MySQL CLI:
mysql --local-infile=0 -u root -p,不能只依赖服务端配置 - Python + PyMySQL:
pymysql.connect(..., local_infile=False),漏掉这参数就白配 - PHP + mysqli:
mysqli_options($conn, MYSQLI_OPT_LOCAL_INFILE, false)必须在mysqli_real_connect()前调用 - Java + Connector/J:8.0+ 默认
allowLoadLocalInfile=false,但若 URL 中显式写了&allowLoadLocalInfile=true,仍会被打开
验证是否真生效,别信配置文件
光看 my.cnf 或 SHOW VARIABLES 不代表安全落地。攻击者会直接试命令,你也得这么验。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 连上后执行:
SELECT @@local_infile;→ 必须返回0 - 立刻执行:
LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE t1;→ 必须报错ERROR 1148 (42000),而不是权限错误或找不到表 - 如果用的是 Web 应用,还要检查其数据库连接库初始化代码里有没有硬编码
local_infile=True(常见于老项目或 ORM 封装层)
容易被忽略的托管环境坑点
自建 MySQL 改完配置重启就完事,但生产中大量用的是托管服务或容器化部署,这里最容易漏检。
- Kubernetes initContainer 或 sidecar 启动的 MySQL 容器,可能覆盖了主配置,
local_infile实际由 entrypoint 脚本动态写入 - AWS RDS / 阿里云 RDS 的参数组修改后,需确认实例“状态”是“正在同步参数”,且等几分钟再查
@@local_infile - Docker Compose 中通过
command: [--local-infile=0]启动,但镜像启动脚本又把该参数覆盖掉了(比如官方镜像的docker-entrypoint.sh会重写mysqld参数)
真正卡住人的,从来不是“怎么关”,而是“以为关了,其实没关”——每次上线前,务必在目标环境中跑一遍 SELECT @@local_infile 和实操 LOAD DATA LOCAL 报错测试。










