LIKE模糊查询防SQL注入必须用参数化+ESCAPE+后端转义三件套:先escape单引号等字符,再将%、_转义为!%、!_,SQL中声明ESCAPE '!'。

LIKE 模糊查询防 SQL 注入,不能靠拼接通配符后直接塞进 SQL 字符串里——这是高危操作,WHERE name LIKE '%{user_input}%' 这种写法一旦没处理好,_、%、单引号、反斜杠全会变成注入入口。
为什么手动拼接通配符容易出事
LIKE 的通配符 % 和 _ 本身是 SQL 语法字符,不是普通数据;如果用户输入里自带 % 或 _,又没被转义,就会改变查询语义。更危险的是:若你用字符串拼接把用户输入包进单引号里(如 '%' + $_POST['q'] + '%'),攻击者输 ' OR '1'='1 就直接破防。
常见错误现象包括:
- 用户搜
abc%,结果把所有以abc开头的记录都查出来(本意是查字面含abc%的字段) - 用户搜
a_c,结果匹配了abc、axc等任意单字符位置 - 返回数据库错误信息,暴露表结构或字段名
PHP 中用 mysqli_real_escape_string() 不够用
mysqli_real_escape_string() 只转义 SQL 元字符(如单引号、反斜杠),但对 % 和 _ 完全不处理——它们在 LIKE 里是功能字符,不是“危险字符”,所以逃逸函数默认放行。
正确做法是:先用 mysqli_real_escape_string() 处理引号等基础注入点,再单独对 % 和 _ 做转义,并在 SQL 中声明 ESCAPE 字符:
- 选一个安全的转义符,比如
! - 把用户输入里的
!先替换成!!(避免和后续转义冲突) - 再把
%替成!%,_替成!_ - SQL 写成:
WHERE name LIKE '!%{$escaped_input}!%' ESCAPE '!'
Java / Python 等语言优先走参数化 + ESCAPE
预编译语句能隔离数据与语法,但 LIKE 的通配符必须出现在参数值里,不能写死在 SQL 模板中。错误示范:"WHERE name LIKE '%?%'" ——问号会被当字面量,不是占位符。
正确写法:
- Java:
String sql = "WHERE name LIKE ? ESCAPE '!';",然后ps.setString(1, "!" + userQuery.replace("!", "!!").replace("%", "!%").replace("_", "!_") + "!"); - Python(PyMySQL):
cursor.execute("SELECT * FROM t WHERE name LIKE %s ESCAPE '!'", [f"!{escaped}!") - MyBatis:
WHERE name LIKE CONCAT('%', #{keyword}, '%')是错的;应改用WHERE name LIKE #{pattern} ESCAPE '!',并在 Java 层构造带转义的pattern字符串
前端传参时就该限制和清洗
后端不该为所有输入兜底。对搜索关键词,在接收时就该做轻量过滤:
- 长度限制在 50 字符内(足够覆盖绝大多数业务场景)
- 拒绝包含
UNION、SELECT、;、--等明显攻击特征的输入(用stripos()或正则快速判断) - 对纯数字 ID 类搜索,强制
intval()或类型转换,彻底切断字符串路径
真正难缠的是既要支持中文/emoji/标点,又要模糊匹配的场景——这时别省事,老老实实用参数化 + ESCAPE + 后端转义三件套。漏掉任意一环,_ 都可能成为盲注突破口。

















