SQL注入高危入口是搜索框直接拼接SQL,必须用参数化查询(如PDO预处理+bindValue或ORM的contains方法),禁用字符串拼接、text()裸写及手动转义,动态字段需白名单控制。

搜索框输入直接拼接SQL就是高危入口
搜索功能是SQL注入的重灾区,因为用户可自由输入任意字符,且往往直接参与WHERE条件构建。一旦用$_GET['q']或request.args.get('q')拼进SQL字符串,攻击者输admin' OR '1'='1或test%'; DROP TABLE products; --就能触发非预期查询甚至删库。
- 即使前端加了JS限制,HTTP请求仍可绕过,后端必须独立校验
- LIKE语句中的
%和_是通配符,不是普通字符,不能简单用filter_var放过 - 全模糊匹配(
WHERE name LIKE '%?%')必须用参数化,不能把%拼进SQL字符串里
PHP搜索场景下必须用PDO预处理+bindValue
用PDO::prepare()和bindValue()是最稳妥的做法,尤其当搜索字段含通配符时,bindValue能确保%被当作数据而非SQL语法解析。
$q = trim($_GET['q'] ?? '');
// ✅ 正确:通配符由PHP拼,参数值走绑定
$stmt = $pdo->prepare("SELECT * FROM products WHERE name LIKE CONCAT('%', ?, '%')");
$stmt->bindValue(1, $q, PDO::PARAM_STR);
$stmt->execute();
<p>// ❌ 错误:把%拼进SQL字符串,再绑定整个字符串,可能被绕过
// $stmt = $pdo->prepare("SELECT * FROM products WHERE name LIKE ?");
// $stmt->execute(["%{$q}%"]); // 若$q含单引号,仍有风险</p>
-
bindValue比execute传数组更可控,尤其对NULL或布尔值 - 避免用
bindParam,它绑定的是变量引用,后续修改变量值会影响已准备语句 - 如果搜索支持多个关键词,拆成
IN (?)并动态生成占位符,不要拼IN ('a','b')
Python Flask/SQLAlchemy中别碰text()和raw SQL
在Flask + SQLAlchemy项目里,text()和session.execute()若带用户输入,等同于裸拼SQL。哪怕用了:q命名参数,若底层驱动未强制参数化(如某些旧版psycopg2配置),依然可能出问题。
# ✅ 安全:ORM query.filter()自动参数化
results = Product.query.filter(Product.name.contains(q)).all()
<h1>✅ 安全:Core select().where()也走参数化</h1><p>stmt = select(Product).where(Product.name.contains(q))</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/ai/2590" title="观猹"><img
src="https://img.php.cn/upload/ai_manual/001/246/273/176907478465059.png" alt="观猹" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/ai/2590" title="观猹" class="overflowclass">观猹</a>
<p class="overflowclass">一款AI工具,主要用于观猹(Watcha.cn)丨AI 产品的大众点评,适合需要提升相关任务效率的用户。</p>
</div>
<a rel="nofollow" href="/ai/2590" title="观猹" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><h1>❌ 危险:text()里写WHERE name LIKE '%:q%',:q会被直接替换,不转义</h1><h1>stmt = text("SELECT * FROM product WHERE name LIKE '%:q%'")</h1>
-
contains()、startswith()、endswith()都生成参数化SQL,放心用 - 真要用
text(),必须配合bindparam(),且确认数据库方言支持 - PostgreSQL的
ILIKE或MySQL的LOWER()包裹字段时,参数仍要走绑定,不能写WHERE LOWER(name) LIKE LOWER('%?%')再拼
搜索词里的单引号、分号、注释符怎么处理?
不需要手动过滤或转义——那是过时且危险的做法。预处理语句本身就把输入当纯数据,'、;、--、/*全都会原样存进字段值里,不会触发语法解析。
- 验证应聚焦业务逻辑:比如限制
$q长度≤100,拒绝空格开头或控制字符 - 若需支持布尔语法(如
price>100 AND stock>0),必须用专用解析器(如lark或pyparsing),绝不能交给SQL执行 - 错误提示别暴露SQL结构,
Invalid search term比Unclosed quote in WHERE clause安全得多
真正难的不是让搜索防注入,而是当需求要求“支持字段名动态切换”或“按用户选的排序字段排序”时,这些无法参数化的部分必须用白名单硬编码,漏掉一个就前功尽弃。










