MySQL不提供SQL模式匹配拦截功能,防护依赖应用层参数化查询与数据库配置协同:启用STRICT_TRANS_TABLES等sql_mode防隐式转换绕过、设secure_file_priv为受限目录封文件操作、禁用local_infile阻客户端文件上传,并最小化账号权限。

MySQL本身不提供“SQL语句模式匹配拦截”功能,无法像WAF那样基于正则或关键字实时阻断 UNION SELECT、SLEEP( 或 1=1 这类模式。所谓“阻止特定SQL语句”,实际只能通过间接手段实现——要么在应用层提前过滤,要么靠数据库配置堵住执行路径,而不是靠MySQL解析SQL内容再做判断。
用 sql_mode 阻止隐式类型转换绕过
攻击者常利用宽松的类型转换绕过预处理防护,比如传入字符串 '1' OR '1'='1' 却被当作整数 1 成功匹配。启用严格模式能切断这类旁路:
-
STRICT_TRANS_TABLES:让插入非法值(如超长字符串、空日期)直接报错,而非静默截断 -
NO_AUTO_CREATE_USER:禁用已废弃的GRANT ... IDENTIFIED BY语法,防止权限误授 -
ERROR_FOR_DIVISION_BY_ZERO:避免除零被转成NULL,干扰布尔盲注判断
设置方式(写入 my.cnf):
sql_mode = STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,ERROR_FOR_DIVISION_BY_ZERO
重启后,原本可能“侥幸通过”的注入变体(如用字符串伪装数字ID)会直接失败。
用 secure_file_priv 封死文件读写通道
很多高危注入依赖 LOAD DATA INFILE 或 SELECT ... INTO OUTFILE 读取配置文件、写入Webshell。只要 secure_file_priv 为空,攻击者就能指定任意路径:
- 设为
/var/lib/mysql-files/(非空且不可写入Web目录) - 确认该目录属主是
mysql,权限为750 - 检查运行时值:
SELECT @@secure_file_priv;,必须返回具体路径,不能是NULL或空字符串
一旦设好,类似 SELECT load_file('/etc/passwd') 或 SELECT '<?php system($_GET[1]);?>' INTO OUTFILE '/var/www/shell.php' 全部失效。
禁用 LOCAL INFILE 防止客户端侧文件上传
即使应用没用 LOAD DATA,攻击者也可能用恶意客户端连接后触发 LOCAL INFILE 读取本地敏感文件(如 ~/.my.cnf)。必须从服务端彻底关闭:
- 启动参数加
--local-infile=0 - 或在配置文件中写
local_infile = OFF - 运行时执行:
SET GLOBAL local_infile = OFF;(需SUPER权限)
验证是否生效:SHOW VARIABLES LIKE 'local_infile'; 应返回 OFF。否则攻击者连上数据库后一句 SELECT * FROM t1 INTO DUMPFILE '/tmp/x' LINES TERMINATED BY '\n'; 就可能配合其他漏洞提权。
别指望触发器或视图“拦截”动态SQL
有人想用触发器监听 DROP TABLE 或 DELETE 并抛错,这是无效的:
- 触发器只对DML(
INSERT/UPDATE/DELETE)生效,对DDL(DROP/CREATE)完全无感知 - 视图只能限制查询字段,无法阻止用户在视图基础上再套一层
UNION SELECT - 存储过程若接受动态SQL参数(如
CONCAT('SELECT * FROM ', table_name)),反而会放大风险
真正可控的防线只有两处:应用层是否用 prepare() + bind_param(),以及数据库账号有没有被授予 FILE、PROCESS、SUPER 这类高危权限。其他所有“拦截模式”的尝试,本质都是在补漏,不是筑墙。


















