后端SQL注入防护需网关层拦截+PHP预处理语句双重保障:网关扫描请求参数中SQL特征并400拦截,签名验证优先于SQL检测;PHP必须用PDO预处理,禁用字符串拼接;order by等动态字段须白名单校验;sqler仅防本地SQLite注入。

后端接口不能只靠参数化查询“兜底”,必须在接入层就拦截恶意SQL特征——否则攻击者用 curl 直打你的API,连Flutter客户端都不用打开。
网关层必须做SQL特征扫描,且顺序不能错
Spring Cloud Gateway 或 OpenResty 等网关是第一道防线。关键不是穷举所有变体,而是快速识别高频攻击载荷:
- 遍历所有
request.getQueryParams()和表单数据,检查值中是否含'、"、;、--、#、UNION、SELECT、EXEC(忽略大小写) - 对 JSON Body,用
Flux<databuffer></databuffer>异步读取后解析为JsonNode,递归遍历所有字符串字段 - 正则匹配布尔盲注(如
OR '1'='1)时,必须加Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE - 命中即返回
400 Bad Request,不抛异常、不打印堆栈,防止信息泄露
顺序错误等于开门揖盗:先验签名,再扫SQL。如果签名非法,参数不可信,直接 401;签名合法但SQL检测失败,说明密钥可能已泄露,要触发告警(企业微信+审计日志),不能只返回 400。
PHP后端必须用预处理语句,不能只靠过滤函数
mysqli_real_escape_string() 或 addslashes() 无法覆盖所有编码绕过场景,比如宽字节注入或多层编码。唯一可靠的是预处理语句:
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ? AND status = ?")-
$stmt->execute([$email, $status])—— 参数不会进入SQL语法树,天然免疫注入 - 即使
$email是admin' OR '1'='1,也只会被当作字符串字面量处理
注意:不要混用——哪怕用了 filter_var($input, FILTER_SANITIZE_STRING),后续仍拼接SQL,照样中招。
Flutter调用PHP API时,order by等动态字段仍是高危点
网关层通常放过 order=created_at 这类参数,但后端若直接拼进SQL:"ORDER BY $order",就可能被用于报错注入或时间盲注。
- 必须用白名单机制校验:
in_array($order, ['id', 'name', 'created_at'], true) - 或改用参数化占位符(部分驱动支持):
ORDER BY CASE ? WHEN 'id' THEN id WHEN 'name' THEN name END - 禁止任何字符串格式化操作,包括
f"ORDER BY {$order}"、"ORDER BY " . $order
这类字段常被忽略,却是线上SQL注入最常突破的缺口。
Dart侧的sqler库能防注入,但仅限本地SQLite
sqler 在Flutter本地数据库(如 sqflite)场景下确实有效:
-
Select(['name']).from('users').where(eq('id', userId))会生成带?占位符的标准SQL - 它自动转义、不拼接字符串,从源头杜绝本地注入
- 但注意:它不处理网络请求参数,也不校验你传给PHP接口的
email字段是否含UNION SELECT
也就是说,sqler 只管本地查库安全,不管你的HTTP请求发了什么——那部分防护必须由网关和PHP后端共同承担。

















