指南

注入 - XSS

跨站脚本攻击(简称 XSS)是另一种注入漏洞,它会导致攻击者控制的脚本在其他用户的浏览器中执行。XSS 也可被视为一种 HTML/JavaScript 注入漏洞。

XSS 漏洞的影响在很大程度上取决于漏洞存在的上下文。其影响范围从提取页面上的信息,到编辑页面状态以受害者身份执行操作,不一而足。

让我们来看看您可能会遇到的 XSS 类型。

XSS 类型

为了便于区分,XSS 可以分为几类。它们根据有效载荷的传递方式和入口点进行分类。

有效载荷传递向量(存储型与反射型)

攻击者可以通过两种方式传递 XSS 有效载荷:

  • 存储型 XSS:通过用户控制的数据(例如存储在数据库中),并在数据呈现给其他用户时触发
  • 反射型 XSS:通过用户提供的有效载荷,该载荷包含在用户浏览的 URL/查询字符串中

关键区别在于,反射型 XSS 通常依赖于用户交互,且仅影响打开包含恶意载荷链接的用户。然而,存储型 XSS 只需用户打开渲染了该载荷的页面,即可针对一名或多名用户执行。

有效载荷位置(DOM 型与非 DOM 型)

有效载荷注入的位置决定了该漏洞是否被归类为“DOM”漏洞。这指的是有效载荷渲染位置的区别:

  • 非 DOM 型 XSS:有效载荷在 HTML 内部渲染,无论是在标签内还是属性内
  • DOM 型 XSS:有效载荷在 JavaScript 内部渲染,例如 `<script>` 标签或事件处理程序(如 `onclick=""`)中

XSS - 防御原语

在本节中,我们将探讨防御 XSS 的基本原则。理解这些基础知识至关重要,但在实践中,您应依赖模板库来提供 99% 的 XSS 防护。

当您编写渲染为标记语言或在浏览器中运行的代码的模板时,防御 XSS 的关键在于*编码*。在此上下文中,编码是指将字符序列转换为解释器以特定方式处理的格式。

但所使用的编码类型取决于数据使用位置或上下文。

  • 标签内,例如 `<div>用户输入内容</div>`:**HTML 编码**
  • 属性内,例如 `<input placeholder="用户输入内容"></input>`:**属性编码**
  • JavaScript 内,例如 `<script>x = "用户输入内容";</script>`:**JavaScript 编码**

一些框架不将编码作为主要的防御手段,而是利用清理(Sanitization)来清除值中任何可能危险的内容。这是一个复杂得多的过程,需要考虑许多边缘情况。不建议自行实现清理程序。

示例

让我们通过不同语言的示例,看看它们在实际应用中是如何运作的。

C# - 不安全:Razor

如果 `IHtmlContent` 对象前缀为 `@`,该值将直接放入模板中,而不进行任何编码。

<!--- 不安全:htmlSnippet 将在不进行任何转义的情况下被解析 --->
@Html.Raw(htmlSnippet)

C# - 安全:Razor

默认情况下,Razor 模板中任何前缀为 `@` 的 `string` 都会进行 HTML 转义。

<!--- 安全:htmlSnippet 将进行 HTML 转义 --->
@htmlSnippet

Java - 安全:JSP

使用 `c:out` 时,默认会进行 XML 转义(可通过 `escapeXml` 属性更改),这在 HTML 上下文中可以防止 XSS,但在其他上下文中则无效。

<div><c:out value="<%= author %>" /></div>

Java - 安全:(fnxml)

与上述类似,你也可以直接调用 `fn:escapeXml`,它会对输入的参数进行 XML 转义。这同样仅在 HTML 上下文中提供保护。

<div>${fn:escapeXml(author)}</div>

Javascript - 不安全:Angular innerHtml

正如其名,指定 `innerHTML` 属性会因禁用输出编码而使你面临 XSS 风险。

<!--- 不安全:htmlSnippet 将在不进行任何编码的情况下被解析 --->
<p [innerHTML]="htmlSnippet"></p>

Javascript - 安全:Angular 插值

Angular 使用双大括号(`{{` 和 `}}`)进行的文本插值会对输出进行 HTML 转义,从而防止 XSS。

<!--- 安全:htmlSnippet 将先进行编码,然后再被解析 --->
<p>{{htmlSnippet}}</p>

Javascript - 不安全:React dangerousInnerHtml

正如其名,指定 `dangerouslySetInnerHTML` 属性会因禁用输出编码而使您面临 XSS 攻击风险。

<!--- 不安全:正如其名,dangerouslySetInnerHTML 属性因不对输出进行转义而具有危险性 --->
<div dangerouslySetInnerHTML={{ __html: htmlSnippet }} />;

Javascript - 安全:React 插值

React 使用花括号(`{` 和 `}`)进行文本插值时,会自动对输出进行 HTML 转义,从而防止 XSS 攻击。

<!--- 安全:htmlSnippet 将被编码并随后被解析 --->
<div>{htmlSnippet}</div>

Python - 不安全:Django

如果在 Django 模板中使用 `safe` 过滤器,它将禁用自动输出转义,从而无法防范 XSS 攻击。

<!--- 不安全:htmlSnippet 不会被 HTML 编码 --->
<div>{{ htmlSnippet | safe }}</div>

Python - 安全:Django

Django 使用双花括号(`{{` 和 `}}`)进行文本插值时,会自动对输出进行 HTML 转义,从而防止 XSS 攻击。

<!--- 安全:htmlSnippet 将被 HTML 编码 --->
<div>{{ name }}</div>

‍

标语

实用安全编码

探索安全编码指南,了解并缓解 OWASP Top 10 等软件漏洞,并通过引导式训练任务在真实的应用程序模拟中进行实践。

更多指南
预约演示