输入验证和输出编码是作用于不同环节的互补手段,而非二选一:输入验证在请求抵达时检查数据合法性,输出编码在数据输出时防止被误解析,二者缺一不可且需按上下文分别设计。

输入验证 和 输出编码 不是“二选一”,而是作用于不同环节的互补手段。单靠一个,防不住真实攻击链。
输入验证只管进来的数据合不合规矩
它在请求刚抵达时检查 req.query.id 是不是正整数、req.body.email 符不符合邮箱格式、req.headers.referer 有没有 SQL 关键字。但它的能力边界很明确:
- 无法阻止合法字符构成的注入,比如
1 OR 1=1对数字字段来说完全“合法” - 对动态拼接场景(如排序字段
ORDER BY ?)无能为力——参数化查询也救不了列名 - 若校验逻辑写在中间件里,但某个接口绕过中间件直取
req.rawBody,验证就失效
输出编码只管数据往外送时会不会被当代码执行
它解决的是“数据在特定上下文里被误解析”的问题。比如把用户昵称 O'Reilly 渲染到 HTML 页面前做 htmlspecialchars(),防止变成 <script></script>;或插入 JSON 响应前用 json_encode() 确保引号不破坏结构。但它不碰数据库查询本身:
- 对
SELECT * FROM users WHERE name = 'xxx'这类语句毫无影响 - 若数据库已执行了恶意查询,编码再严也拦不住数据泄露
- 错误编码(比如在 SQL 上下文中用 HTML 编码)反而可能引入新漏洞
为什么必须前后端协同做这两件事?
因为攻击者会沿着整个数据流找最薄弱的一环下手:
- 前端
pattern校验可被禁用 JS 绕过,但配合后端input validation能卡住批量爬虫和低级 payload - 后端对
req.query.q做白名单过滤(如只允许字母+空格),再交给PreparedStatement执行,比单纯依赖参数化更稳 - 日志系统记录原始
req.query.q前若不做escape或脱敏,可能触发log4j类日志注入
JSON.parse() 解析的 webhook body 没做字段类型校验、ORM 的 raw() 方法绕过了参数化——这些地方,输入验证 和 输出编码 都得按上下文重新设计,不能复用同一套规则。











