5月27日 21:29

输入验证和输出编码有什么区别?如何正确使用它们来防止 XSS 攻击?

核心区别

输入验证在数据进入系统时检查合法性,输出编码在数据输出到页面时转义危险字符。两者不可互相替代——验证拦不住所有恶意输入,编码兜底保证即使漏网也不会执行。

一句话:验证是门卫,编码是防弹衣。

输入验证

在接收用户输入的环节,对数据的格式、类型、长度做白名单校验,拒绝不符合预期的内容。

javascript
// 白名单:只允许字母数字 function validateUsername(name) { return /^[a-zA-Z0-9]+$/.test(name); }

关键原则:

  • 用白名单,不用黑名单——黑名单永远补不完绕过方式
  • 服务端必须验证,客户端验证可被绕过
  • 验证的是"数据是否合法",不是"数据是否危险"

输出编码

在数据渲染到页面之前,根据输出上下文对特殊字符做转义,使浏览器将其视为文本而非代码。

javascript
// HTML上下文:转义 < > " ' & function escapeHtml(str) { return str .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;'); }

不同上下文需要不同编码:HTML正文、HTML属性、JavaScript内联、URL参数、CSS——各用对应的转义函数或库(如 DOMPurify 处理 HTML,encodeURIComponent 处理 URL)。

为什么两者缺一不可

  • 只做验证:攻击者总能找到绕过方式(编码变形、特殊字符组合),验证无法覆盖所有场景
  • 只做编码:恶意数据会进入数据库,污染日志、影响其他系统、触发二阶XSS
  • 实际流程:用户输入 → 输入验证 → 存储 → 输出编码 → 渲染,每一层各自兜底

模板引擎默认编码(EJS <%=%>、Handlebars {{}})能挡住大部分HTML上下文XSS,但JavaScript上下文和URL上下文仍需手动处理。

追问:CSP 能替代输出编码吗?

不能。CSP(Content Security Policy)限制脚本加载来源,是纵深防御的一环,但无法防御同源内的内联脚本注入,且旧浏览器不支持。CSP 是保险绳,输出编码是安全带,都得系。

标签:XSS