准则

注入 - XSS

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

XSS 漏洞的影响在很大程度上取决于漏洞存在的环境。影响范围包括从脚本运行的页面中提取信息,以及编辑页面状态,以受害者的身份在页面上执行操作。 

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

XSS 类型

XSS 可分为几类,以帮助区分它们。它们根据有效载荷的传输方式和入口点的位置进行分类。

有效载荷传输载体(存储与反射)

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

  • 存储型 XSS:通过存储在数据库等中的用户控制数据,将数据呈现给其他用户
  • 反射式 XSS:通过用户浏览的 URL/查询字符串传递用户提供的有效载荷

关键区别在于,反射式 XSS 通常取决于用户交互,只对打开带有恶意有效载荷链接的用户产生影响。然而,存储型 XSS 只需打开一些呈现有效负载的页面,就能对一个或多个用户执行。 

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

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

  • 非 DOM XSS:有效载荷在 HTML 内呈现,无论是在标记内还是在属性内
  • DOM XSS: The payload is rendered inside JavaScript, like a `<script>` tag, or an event handler such as `onclick=""` 

XSS - 防御基元

本节将介绍防御 XSS 的基本原理。了解其中的基本原理至关重要,但在实践中,您应该依靠模板库来实现 99% 的 XSS 防护。

当你编写的模板被呈现为标记或代码在浏览器中运行时,防止 XSS 的关键在于*编码*。在这里,编码指的是将字符序列转换成一种格式,并由解释器以特定方式进行处理。 

但使用哪种编码取决于使用数据的位置或环境。 

  • Inside a tag, like `<div>User input here</div>`: **HTML Encoding**
  • Inside an attribute, like `<input placeholder="User input here"></input>`: **Attribute Encoding**
  • Inside Javascript, like `<script>x = "User input here";</script>`: **JavaScript Encoding**

有些框架会放弃将编码作为主要防御手段,转而使用 sanitization 来清除任何可能存在危险的内容值。这是一个复杂得多的过程,需要考虑许多边缘情况。我们不建议您自行实施消毒例程。

实例

让我们看看不同语言中的一些示例,看看它们的实际效果如何。

C# - 不安全:剃刀

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

<!--- UNSAFE: The htmlSnippet will get interpreted without any escaping --->
@Html.Raw(htmlSnippet)

C# - 安全:剃刀

默认情况下,在Razor模板中,任何以"@"为前缀的 "字符串 "都会被HTML转义。 

<!--- SAFE: The htmlSnippet will get HTML escaped --->
@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 风险。

<!--- UNSAFE: The htmlSnippet will get interpreted without any encoding --->
<p [innerHTML]="htmlSnippet"></p>

Javascript - 安全:Angular 插值

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

<!--- SAFE: The htmlSnippet will get encoded and then interpreted --->
<p>{{htmlSnippet}}</p>

Javascript - 不安全:React dangerousInnerHtml

顾名思义,指定 "dangerouslySetInnerHTML "属性后,就会因禁用输出编码而面临 XSS 风险。

<!--- UNSAFE: As the name suggests, the dangerouslySetInnerHTML attribute is dangerous as it does not escape the output --->
<div dangerouslySetInnerHTML={{ __html: htmlSnippet }} />;

Javascript - 安全:React 内插

React 使用大括号(`{` 和 `}`)对文本进行插值时,会对其输出进行 HTML 转义,以防止 XSS。 

<!--- SAFE: The htmlSnippet will get encoded and then interpreted --->
<div>{htmlSnippet}</div>

Python - 不安全:Django

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

<!--- UNSAFE: The htmlSnippet will not get HTML encoded --->
<div>{{ htmlSnippet | safe }}</div>

Python - 安全:Django

Django 使用双大括号(`{{` 和 `}}`)对文本进行插值时,会对其输出进行 HTML 转义,以防止 XSS。

<!--- SAFE: The htmlSnippet will HTML encoded --->
<div>{{ name }}</div>