sql注入总在搜索框爆发,因搜索功能需拼接用户输入且开发者常误用单引号过滤或like误判为安全;正确做法是严格使用参数化查询,如flask sqlalchemy用filter()传独立参数、mybatis强制用#{}、禁用字符串拼接与前端校验替代。

SQL注入为什么总在搜索框爆发
因为搜索功能天然需要拼接用户输入,而开发者常误以为“加个单引号过滤”或“用 LIKE 就安全”。实际只要没走参数化查询,WHERE title LIKE '%{user_input}%' 这种字符串拼接,遇到 ' OR 1=1 -- 就直接崩。重点不是“搜得准”,是“搜得不被脱库”。
Python Flask + SQLAlchemy 怎么做真正参数化封装
别碰 text() 或 execute() 手动拼 SQL;也别用 format() 或 f-string 插入关键词。正确路径只有一条:让 ORM 或底层驱动接管变量绑定。
-
filter()中所有动态值必须作为独立参数传入,例如:Article.query.filter(Article.title.like(f'%{keyword}%'))❌(f-string 仍属拼接);应写成Article.query.filter(Article.title.like('%' + keyword + '%'))✅(+ 操作在 Python 层,SQL 参数仍由 SQLAlchemy 绑定) - 更稳妥的是显式用
bindparam:Article.query.filter(Article.title.like(bindparam('kw', type_=String))),再调用.params(kw=f'%{keyword}%') - 若必须用原生 SQL,请用
session.execute(text("SELECT * FROM article WHERE title LIKE :kw"), {"kw": f"%{keyword}%"})—— 冒号前缀 + 字典传参,才是参数化
Java MyBatis 的 #{} 和 ${} 到底怎么选
这是高频翻车点。${} 是字符串替换,#{} 才是预编译占位符。搜索框里所有用户输入,必须无条件用 #{}。
- 错误写法:
WHERE title LIKE '%${keyword}%'→ 输入abc' OR '1'='1直接触发注入 - 正确写法:
WHERE title LIKE CONCAT('%', #{keyword}, '%')(MySQL)或WHERE title LIKE '%' || #{keyword} || '%'(PostgreSQL) - 注意:MyBatis 默认对
#{}做预编译,但如果你在<script></script>标签里又手动拼了字符串,等于白设
前端传参和后端校验的边界在哪
前端限制输入长度、过滤特殊字符(如 '、;、--)只是辅助,不能替代服务端参数化。真正不可绕过的是:后端收到 keyword 后,不加任何“转义”“替换”“正则清洗”,直接进参数化流程。
- 别写
keyword.replace("'", "''")—— 这是 SQL Server 的转义逻辑,对 MySQL/PostgreSQL 无效,且容易漏掉其他绕过方式(如宽字节、编码混淆) - 别信“我只允许中文和字母”——攻击者可以用注释符、空字节、Unicode 变体绕过前端校验
- 关键动作只有两个:接收原始字符串 → 丢进参数化查询接口 → 交给数据库驱动处理
参数化本身不解决模糊匹配的性能问题,但它是安全底线。很多人加了 #{} 却还在 SQL 里写 LIKE '%xxx%' 导致全表扫描,那是另一个问题;而没加参数化,连上生产就是裸奔。











