标准答案
- 所有用户输入的值,包括 ID、搜索词、日期和分页游标,都应作为绑定参数传递,不能通过字符串拼接进入 SQL。
- 预编译或参数绑定能让数据库明确区分查询模板与参数值,输入中的引号、注释和关键字不会改变 SQL 语义。
- ORDER BY 的列名和 ASC 或 DESC 属于 SQL 结构,通常不能用普通占位符替代,应映射到固定允许列表。
- 动态筛选条件应由受控的条件构造器组合,字段名、操作符和排序都来自枚举,而不是直接信任请求参数。
- 参数化仍要配合最小数据库权限、错误信息控制和审计,不能把它当作唯一安全措施。
题目解析
许多注入漏洞不是发生在 WHERE name = ? 这种明显位置,而是排序、字段选择、批量导出或后台筛选功能。开发者以为“传的是排序字段不是值”而直接拼接,攻击者就能控制 SQL 结构。
白名单应映射业务 API 名称到真实列名。例如公开参数 sort=created 映射为 created_at,而不是允许调用方传任何数据库列,这也能避免内部字段被意外暴露。
代码示例
排序字段通过固定映射选择,用户输入只决定允许的业务选项。
TypeScript
const sortColumns = { newest: 'created_at', amount: 'amount' } as const
const column = sortColumns[input.sort as keyof typeof sortColumns] ?? 'created_at'
const direction = input.order === 'asc' ? 'ASC' : 'DESC'
const sql = `SELECT id, amount FROM orders WHERE tenant_id = ? ORDER BY ${column} ${direction}`
await db.query(sql, [tenantId])常见误区
- 只给字符串加引号或手动转义,仍让输入参与 SQL 结构拼接。
- 认为 ORM 一定自动防注入,在 raw SQL、动态字段或模板查询中放松审查。