必须同时禁用服务端和客户端local_infile,仅关一端无效;真实生效需满足:select @@local_infile返回0,且load data local infile '/etc/passwd'报error 1148或2068。

必须同时禁用服务端和客户端两侧的 local_infile,只关一端等于没关。
检查当前 local_infile 是否真被禁用
别只信配置文件或 SHOW VARIABLES LIKE 'local_infile' 的返回值——它可能显示 OFF,但客户端连接时仍能绕过。真实生效要满足两个条件:
-
SELECT @@local_infile;返回0(注意:是会话级变量,需在你实际连接的用户下执行) - 用该连接执行
LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE t;,必须报错ERROR 1148 (42000)或ERROR 2068 (HY000) - 如果返回空、
NULL或ON,说明服务端配置未加载(常见于写错段落,比如把local_infile = 0写进了[mysql]而非[mysqld])
服务端禁用:改配置并重启,SET GLOBAL 不顶用
SET GLOBAL local_infile = OFF 是临时的,MySQL 重启后恢复默认(尤其旧版本或某些 Docker 镜像仍默认 ON)。真正落地得改配置文件:
- 编辑配置文件:
/etc/my.cnf(CentOS)、/etc/mysql/mysql.conf.d/mysqld.cnf(Ubuntu/Debian)或 Windows 的my.ini - 在
[mysqld]段下添加:local_infile = OFF(或0),不能写在[client]或[mysql]段 - 确认没有其他段(如
[server]、[mysqld_safe])覆盖该设置 - 执行
sudo systemctl restart mysqld(不是reload)
客户端禁用:不同工具写法完全不同
服务端关了,客户端还开着,攻击者就能通过 SQL 注入让应用主动发起本地读取。各客户端关闭方式互不兼容,漏一个就留后门:
- MySQL CLI:启动时加
--local-infile=0;或在~/.my.cnf的[client]段写local_infile = 0(部分版本忽略该配置,优先用命令行参数) - PyMySQL:
pymysql.connect(..., local_infile=False)—— 注意不是默认值,部分二进制包默认裁掉支持 -
mysql-connector-python:
mysql.connector.connect(..., allow_local_infile=False) - PHP mysqli:
mysqli_options($conn, MYSQLI_OPT_LOCAL_INFILE, false),且必须在mysqli_real_connect()前调用 - Java JDBC URL 中加:
&allowLoadLocalInfile=false
别忘了 secure_file_priv 和 FILE 权限这两层
local_infile = OFF 只管 LOAD DATA LOCAL INFILE,不管服务端自己读文件。要防服务器本地泄露,还得补两刀:
- 在
[mysqld]段加secure_file_priv = NULL并重启,验证SELECT @@secure_file_priv;返回NULL - 执行
REVOKE FILE ON *.* FROM 'user'@'host';,特别检查root用户:SELECT User,Host,File_priv FROM mysql.user WHERE File_priv='Y' - 之后再试
LOAD DATA INFILE '/etc/passwd' INTO TABLE t;,应报ERROR 1290
最常被忽略的是:Docker 启动时加了 --local-infile 参数,会覆盖配置文件;云数据库(如阿里云 RDS)虽提供参数组开关,但修改后未必实时生效,上线前必须手动连上去跑 SELECT @@local_infile; 实测。











