thinkphp中直接字符串拼接like查询必然导致sql注入,必须改用参数化方式如where(['name'=>['like','%'.$q.'%']])或wherelike('name','%'.$q.'%'),严禁出现"%".$q."%"类写法。

直接用字符串拼接构造 LIKE 查询,就是把数据库大门钥匙塞给用户——无论你后面加多少过滤,都拦不住 ' OR 1=1 -- 这类 payload。
用参数化查询替代 "%"+q+"%" 拼接
所有语言里最根本的防线:把用户输入当“数据”传进去,而不是当“代码”拼进去。一旦拼进 SQL 字符串,就等于放弃防御主动权。
- PHP(PDO)必须走
prepare()+bindValue(),$q只能作为参数,不能出现在"WHERE title LIKE '%".$q."%'"这种字符串里 - Python(Django)优先用
filter(title__icontains=q);手写 raw SQL 时,必须用cursor.execute("SELECT ... WHERE title LIKE %s", ['%' + q + '%']) - Java(JDBC)禁用
Statement,改用PreparedStatement,setString(1, "%" + q + "%")是安全的," LIKE '%" + q + "%'"是危险的 - ThinkPHP6 别手写 SQL,用
whereLike或带问号的绑定写法:where('name like ?', ['%' . $search . '%'])
MyBatis 中别用 ${username},改用 #{username} 包裹通配符
${} 是字符串替换,等同于拼接;#{} 是预编译参数占位,底层走 PreparedStatement。但注意:仅靠 #{} 不够,因为 % 和 _ 在 LIKE 里仍是通配符,会引发语义绕过。
- 错误写法:
WHERE username LIKE '%${username}%'—— 直接执行恶意字符串 - 正确写法一(推荐):
WHERE username LIKE concat('%', #{username}, '%'),让数据库拼接,参数仍受保护 - 正确写法二:
WHERE username LIKE #{pattern},由 Java 层提前拼好'%' + q + '%'再传入,确保#{pattern}是完整值 - 如果业务允许,避免前导
%,改用#{username} + '%'(后缀匹配),减少盲注面
MySQL/PostgreSQL 中对 LIKE 里的 %、_ 做转义
即使用了参数化,LIKE 的通配符逻辑仍在生效。用户搜 admin%,可能意外匹配到 administrator,甚至被用于构造布尔盲注。
- MySQL:在
LIKE后加ESCAPE '',并把输入中的%、_、\提前转义,例如:WHERE name LIKE '%%%' ESCAPE '' - PostgreSQL:用
ESCAPE E''',且需确认客户端连接编码为 UTF-8,否则双字节字符可能绕过转义 - 更稳妥的做法:业务侧限制搜索长度(如 ≤20 字符)、拒绝含
%、_、的输入,或改用全文索引(MATCH ... AGAINST/to_tsvector)
ThinkPHP6 的 whereLike 不是万能解药
它确实自动转义 % 和 _,也做参数绑定,但前提是:你得真正用它,而不是绕开框架自己拼 SQL。
- 正确用法:
$map['name'] = ['like', '%' . $q . '%']; Db::name('user')->where($map)->select() - 错误用法:
Db::query("SELECT * FROM user WHERE name LIKE '%{$q}%'")—— 完全失效 - 注意边界:如果前端传入的是已含
%的关键词(比如用户想搜字面量test%),whereLike默认不会帮你识别这是字面意图,仍会当作通配符处理,此时必须配合手动转义或换用whereRaw+ 显式ESCAPE
最常被忽略的一点:参数化只解决“执行注入”,不解决“语义注入”。LIKE 本身的设计就是把 % 当指令,只要它还在查询逻辑里,就存在被滥用的风险——要么收敛使用场景,要么换技术方案。











