订单查询接口SQL注入漏洞必须用参数化查询修复,禁用字符串拼接;需严格校验订单ID类型(数字型校验正整数,字符型限长+白名单);MyBatis中禁用${},统一用#{};多条件查询应使用QueryWrapper等安全构建器;数据库账号仅授SELECT权限并关闭错误回显。

订单查询接口的SQL注入漏洞,99%是因直接拼接用户输入参数导致的,修复必须立刻停用字符串拼接,改用参数化查询——这是唯一可靠解法,其他过滤、黑名单、正则匹配都只是临时补丁,挡不住绕过。
订单ID参数是数字型还是字符型?先确认类型再选方案
很多开发者一上来就写 #{orderId} 或 ? 占位符,却没验证参数实际类型。如果接口允许传 id=123' OR '1'='1,说明后端没做基础类型校验,哪怕用了参数化,也可能因类型转换失败 fallback 到拼接逻辑。
- 数字型(如
id=100):严格校验是否为正整数,非数字直接拒收,不走数据库查询路径 - 字符型(如
orderNo=ORD20260812-ABC):需额外限制长度(如 ≤32)、白名单字符(仅允许字母、数字、短横线),拒绝任何单引号、分号、注释符 - 若接口同时支持两种格式(如
?q=100或?q=ORD-xxx),必须拆成两个独立参数校验,不能共用一个字段做模糊匹配
MyBatis里用 #{} 还是 ${}?错用 ${} 就等于开门放贼
若依、Spring Boot 项目中大量使用 MyBatis,${} 是最常被误用的高危点。它不做任何转义,直接把变量值插入 SQL 字符串——攻击者输入 1' UNION SELECT password FROM users--,就会原样执行。
-
#{}是安全的:MyBatis 自动转义并生成 PreparedStatement 参数占位符,底层交给 JDBC 处理 -
${}只能用于动态表名、字段名等极少数场景,且必须配合白名单校验(如if (tableName.equals("order_2026") || tableName.equals("order_2025"))) - XML 中检查所有
<if test="xxx != null"> AND xxx = '${xxx}'</if>,全部替换成#{xxx}
WHERE 条件里多个参数拼接?别手写 SQL,用 QueryWrapper 或 CriteriaBuilder
订单查询常带多条件筛选(状态、时间范围、用户ID),开发者容易写成 "WHERE status = '" + status + "' AND create_time >= '" + startTime + "'"。这种写法无论加多少层过滤都不可靠。
- Spring Boot + MyBatis-Plus 推荐用
QueryWrapper<Order>:每个条件调用eq()、ge()等方法,内部自动参数化 - 纯 MyBatis XML 中避免
<where>块内混用${}和#{},所有变量统一用#{} - 若必须动态列名(如按不同租户查不同字段),先从配置中心读取允许字段列表,再用
String.format()拼接列名,参数值仍走#{}
上线前漏掉的致命细节:数据库账号权限和错误信息暴露
参数化修好了,不代表万事大吉。攻击者仍可能通过报错信息反推表结构,或利用高权限账号拖库。
- 订单库账号必须只授予
SELECT权限(甚至只给特定视图),禁止DROP、INSERT、UNION所需的额外权限 - 关闭数据库错误回显:MySQL 设置
sql_mode=STRICT_TRANS_TABLES,禁用show errors;应用层捕获SQLException后只返回通用提示(如“查询失败”),绝不透出You have an error in your SQL syntax这类原文 - 日志里记录原始参数(如
orderId=123' OR 1=1),但不要记录完整 SQL,防止敏感字段泄露
最易被忽略的是:参数化只解决“数据”注入,但无法防御“结构”注入——比如动态表名、排序字段、分页参数 ORDER BY ${sortField}。这些地方必须单独校验,且不能依赖前端传值。

















