输入净化

从用户输入中移除或中和有害代码和无效字符的过程。防御 XSS 时要与针对输出目标的转义配合使用。

输入净化是从用户输入数据中移除或中和有害代码和无效字符串的过程。它是 Web 应用安全中最基本的防御手段之一,基于"永远不信任输入,必须验证和转换"的原则。如果忽略净化处理,攻击者可以通过表单或 URL 参数注入恶意代码,导致数据泄露或系统被入侵。

在 XSS (跨站脚本) 攻击中,恶意 JavaScript 代码通过用户输入嵌入到网页中。例如,如果在论坛的发帖栏中输入 <script>alert('XSS')</script>,在没有净化处理的情况下,该脚本会在其他用户的浏览器中执行。SQL 注入则是将恶意 SQL 语句插入数据库查询中,导致数据篡改或泄露。

净化方法有多种途径。HTML 转义将特殊字符转换为实体引用,将 < 替换为 &lt;,将 > 替换为 &gt;。白名单方式只允许许可的字符或标签通过,适用于安全显示富文本编辑器的输出。黑名单方式移除禁止的模式,但由于无法应对未知的攻击模式,白名单方式被认为安全性更高。在允许用户有意书写 HTML 的功能上,比起自己搭一套许可列表,使用久经检验的 HTML 净化库才是定式,OWASP 推荐 DOMPurify。这类库会追随新的绕过手法而持续修正,因此定期更新是前提。另外,对净化之后的字符串再动手加工,防御就可能失效,所以要把净化放在输出前的最后一道工序。

在实际开发中,净化通常与验证 (检查输入值是否符合预期格式) 配合使用。验证判断"输入是否符合预期格式",净化则"将输入转换为安全格式"。这里容易搞错的是处理所在的位置。OWASP 的 XSS 对策指引把"在数据被渲染的地方尽可能近的位置 (也就是输出的那一瞬间) 做转义"作为原则。在接收输入的时刻就一律做 HTML 转义再保存的设计,是在还不知道该值最终会输出到 HTML、JavaScript 还是 URL 的情况下就做了转换,因此会漏掉本该有的防御,或者让 O'Hara 这类正当输入因重复转义而损坏。输入时该做的是格式验证 (检查允许的字符种类与长度),转义按输出目标分别进行,这样把职责分开就不会混乱。多数框架都具备模板引擎在输出时自动转义的机制,使用它比自行实现更安全。

净化和转义经常被混淆,但严格来说是不同的概念。净化是移除或转换危险元素的综合处理,而转义是将在特定上下文 (HTML、SQL、URL 等) 中具有特殊含义的字符转换为安全表示的处理。根据输出目标的上下文选择适当的转义方式至关重要:HTML 输出使用 HTML 转义,SQL 查询使用参数化查询 (预处理语句)。在 OWASP 的 SQL 注入对策中,预处理语句与变量绑定被置于第一道防御,而把输入转义后再嵌入 SQL 语句的做法则被列为"强烈不推荐" (因为各数据库产品的规则不同,很容易出问题)。对于 SQL,正确的答案不是在净化上苦撑,而是换到把代码与数据分离的机制上去。

从字符计数角度看,净化处理可能改变字符串长度。例如 < 转换为 &lt; 时 1 个字符变为 4 个,& 变成 &amp; 是 5 个," 变成 &quot; 是 6 个。在设计数据库列长度或表单字符数限制时,需要考虑净化后的字符串长度。用户输入的字符数与净化后存储在数据库中的字符数不一致,是常见的 bug 来源。作为设计上的准则,输入的字符数限制要按用户看到的原始字符串来判定 (若按转义后的长度来拒绝,就无法解释为什么会报错),数据库的列长度则要按转义后的最坏情况 (1 个字符变成 6 个) 预留,这样把职责分开就不容易出事故。

分享这篇文章