搜索框SQL注入需三重防护:①强制参数化查询(PDO/Django/PreparedStatement);②LIKE通配符转义并限制匹配模式;③白名单校验输入+收敛错误响应。

搜索框是SQL注入高发区,因为用户输入直接拼进 LIKE 查询或全文检索语句里,稍不注意就会执行 ' OR 1=1 -- 这类 payload。修复不能只靠“过滤单引号”,得从查询构造方式、字符处理逻辑、上下文逃逸三方面同时堵住。
用参数化查询替代字符串拼接
搜索功能最常犯的错,就是把 $_GET['q'] 或 request.GET.get('q') 直接插进 SQL 字符串里,比如 "SELECT * FROM article WHERE title LIKE '%" + q + "%'"。这种写法无论后面加多少过滤都拦不住绕过。
- PHP(PDO)必须用
prepare()+bindValue(),q只能作为参数传入,绝不能出现在 SQL 字符串中 - Python(Django)优先走
filter(title__icontains=q),底层自动参数化;若手写 raw SQL,必须用cursor.execute("SELECT ... WHERE title LIKE %s", ['%' + q + '%']) - Java(JDBC)禁用
Statement,改用PreparedStatement,setString(1, "%" + q + "%")是安全的,executeQuery("... LIKE '%" + q + "%'")是危险的
LIKE 查询中的通配符必须转义
即使用了参数化,LIKE 本身会把 % 和 _ 当作通配符,攻击者可利用这点做盲注或扩大匹配范围。比如用户搜 admin%,可能意外匹配到 administrator 或其他敏感词前缀。
- MySQL 中需在
LIKE子句后加ESCAPE '',再对q中的%、_、替换为%、_、\ - PostgreSQL 用
ESCAPE E'\',且要确保客户端连接编码为 UTF-8,否则双字节字符可能绕过转义 - 如果业务允许,直接禁用模糊前缀匹配(即不用
%" + q + "%),改用q + "%"(后缀匹配)或全文索引(MATCH ... AGAINST),减少通配符暴露面
白名单过滤比黑名单更可靠
试图用正则删掉 union、select、sleep 等关键字,基本无效——大小写变形、注释符绕过(se/*abc*/lect)、宽字节编码都能穿过去。真正可控的是限定输入范围。
- 纯中文搜索:用
preg_match('/^[x{4e00}-x{9fa5}a-zA-Z0-9s-.,!?]+$/u', $q)校验,拒绝一切非预期字符 - 英文+数字ID类搜索(如订单号):强制
ctype_alnum($q)或正则/^[a-zA-Z0-9-_]+$/ - 若必须支持符号(如邮箱、URL),应拆解字段单独处理,而不是一股脑塞进
WHERE ... LIKE
错误信息和响应行为要收敛
搜索框被注入后,数据库报错(如 MySQL Error 1064)或响应时间突增(基于时间的盲注),都会给攻击者反馈。这些不是“修完代码就完事”的环节,而是最后一道防线。
- 关闭所有环境的详细错误输出(PHP 的
display_errors=Off,Django 的DEBUG=False) - 统一返回空结果或“未找到”,避免用
if (mysql_num_rows() == 0)区分“无结果”和“语法错误” - 对高频异常查询(如含
1=1、benchmark(、information_schema)做 Nginx 层限流或直接 403,不交到应用层
搜索框的 SQL 注入最难缠的地方在于:它往往混合了前端 JS 拼接、后端多层 ORM 转换、数据库函数嵌套(如 CONCAT、REPLACE),任何一个环节脱节,前面做的参数化就白费。重点盯住“谁在拼 SQL 字符串”和“谁在决定 ESCAPE 字符”,这两处出问题,其他加固都成摆设。

















