注入 - 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>