SQL动态条件拼接最核心的安全原则是所有用户输入必须参数绑定,仅固定可信的结构部分(字段名、表名、白名单操作符)可拼接,其余一律绑定。

SQL动态条件拼接最核心的安全原则是:所有用户输入必须通过参数绑定传递,绝不能直接拼接进SQL字符串。关键不在于“怎么拼”,而在于“哪些内容能拼、哪些必须绑”。
哪些可以安全拼接?
只有**固定、可信、无用户参与的结构部分**可拼接,比如字段名、表名、操作符(需白名单校验)、逻辑连接词(AND/OR)。
- 字段名:如 "user_name"、"status" —— 必须来自预定义枚举或严格正则校验(如 ^[a-zA-Z_][a-zA-Z0-9_]*$)
- 表名:如 "orders"、"products" —— 只能从配置列表中选取,禁止任何外部传入
- 操作符:如 "="、"LIKE"、">" —— 必须限定在白名单内,不可接受 red">"= 1 OR 1=1" 类注入式输入
哪些必须参数绑定?
**所有用户输入值**——无论来源(URL参数、表单、API Body、Cookie),都必须走参数化。包括数字、字符串、日期、布尔、NULL(用 IS NULL 判断,不绑 NULL 值)。
- 搜索关键词:WHERE name LIKE ? → 绑定 "%张%",而非拼成 "name LIKE '%张%'"
- 范围查询:WHERE create_time BETWEEN ? AND ? → 绑定两个 DateTime 对象,不拼时间字符串
- IN 列表:若数量固定(如状态枚举),用多个 ? 占位;若数量动态,需按实际个数生成占位符并批量绑定(如 WHERE id IN (?, ?, ?))
常见错误拼接场景避坑
这些看似方便的操作,实为高危漏洞温床:
- WHERE status = ${status} —— 模板引擎变量插值 ≠ 参数绑定,${} 会被直接替换,等同字符串拼接
- ORDER BY ${sortField} ${sortDir} —— 排序字段和方向必须双重白名单(如 sortField ∈ ["id","name","ctime"],sortDir ∈ ["ASC","DESC"])
- IN (${idList}) —— 即使 idList 是数字数组,也不能直接展开为逗号分隔字符串;应生成对应数量 ? 占位符后绑定
推荐实践结构(伪代码示意)
用“拼结构 + 绑值”双阶段分离逻辑:
- 初始化空条件列表、空参数列表
- 遍历查询参数:校验字段名/操作符 → 追加 SQL 片段到条件列表 → 将值推入参数列表
- 组合主SQL:SELECT * FROM users WHERE 1=1 + JOIN conditions
- 执行时一次性传入全部参数数组(如 JDBC 的 PreparedStatement.setObject(i, param))










