MySQL中LIMIT/OFFSET、ORDER BY字段名、LIKE通配符位置及Oracle INSERT VALUES绑定变量均不可安全参数化,须通过白名单校验、应用层拼接、改用SELECT子句等方式规避SQL注入与解析失效风险。

MySQL 中 LIMIT 和 OFFSET 不能参数化
MySQL 5.7+ 默认不支持 LIMIT ? 或 OFFSET ? 这类写法,除非连接 URL 显式开启 useServerPrepStmts=true。即使开了,某些旧版驱动(如 mysql-connector-java 5.x)仍可能 fallback 到字符串拼接。错误现象是抛出 SQLSyntaxErrorException: You have an error in your SQL syntax,或静默执行全表扫描。
实操建议:
- 不要依赖驱动自动“修复”,始终在应用层校验原始输入是否为纯数字字符串(正则
^[0-9]+$),再转int并限定范围(如1–1000) - 校验失败时直接返回
400,不 fallback 到默认值 - 若必须动态控制分页,优先用 MyBatis 的
#{size}+#{offset}(确认 XML 中未混用${})
ORDER BY 字段名无法参数化
数据库不允许把列名、表名、函数名这类“结构标识符”用 ? 或 $1 绑定——它们不是数据值,而是 SQL 解析阶段就要确定的语法元素。常见错误是传入 sort=id DESC, (SELECT password FROM users),后端只校验了 sort 是字符串,就拼进 SQL:ORDER BY ? 会报错,而 ORDER BY ${sort} 直接注入。
实操建议:
- 对所有用户可控的排序字段,必须使用白名单校验(如只允许
["id", "created_at", "name"]) - 白名单要包含完整字段表达式,比如
"name ASC"和"name DESC"视为两个独立项,不可仅校验前缀 - 避免 ORM 动态 SQL 中 fallback 到字符串拼接(如 PageHelper 在某些条件下会绕过
#{})
LIKE 子句中的通配符位置不能由参数决定
参数绑定只能传递数据值,不能决定通配符放在前、后还是中间。写成 WHERE name LIKE ? 是安全的,但若想实现“前缀匹配”和“后缀匹配”两种逻辑,不能靠传入 "%abc" 或 "abc%" 来切换——因为业务逻辑已耦合到参数内容里,容易被绕过(如用户传 "%abc' OR 1=1--")。
实操建议:
- 通配符拼接必须在应用层完成,且仅限固定模式:如前缀查就统一用
"%" + input.trim() + "%",再绑定整个字符串 - 禁止在 SQL 中用
CONCAT('%', ?, '%')等函数“试图增强灵活性”,它不增加安全性,反而降低可读性 - 若需支持多种模糊模式(如开头/结尾/任意位置),应拆成不同接口或用枚举参数控制,而非放行任意字符串
Oracle 中 INSERT ... VALUES 的绑定变量不被捕获
Oracle 从 10g 起就明确不捕获 INSERT INTO t(col) VALUES(?) 中 VALUES 子句里的绑定变量值,无论硬解析还是软解析。这意味着你查 v$sql_bind_capture 永远看不到这些值,排查慢插入或数据异常时会断档。
实操建议:
- 对关键
INSERT语句,改用INSERT ... SELECT形式,把值来源转为子查询,这样绑定变量可被捕获 - 日志中主动记录实际插入值(注意脱敏),不要依赖 Oracle 自动捕获
- 避免在性能分析时只盯
v$sql_bind_capture,需结合应用层日志交叉验证
#{sortField},但 sortField 是个 Map 对象,JPA 原生查询漏了 @Param 导致退化为位置绑定——这些地方既不报错,又悄悄失效。

















