- 防止跨站点脚本 (XSS) 在 ASP.NET CorePrevent Cross-Site Scripting (XSS) in ASP.NET Core
- 针对 XSS 保护应用程序Protecting your application against XSS
- 使用 Razor 的 HTML 编码HTML Encoding using Razor
- 使用 Razor 的 JavaScript 编码JavaScript Encoding using Razor
- 在代码中访问编码器Accessing encoders in code
- 编码 URL 参数Encoding URL Parameters
- 自定义编码器Customizing the Encoders
- 编码应发生在何处?Where should encoding take place?
- 验证为 XSS 保护方法Validation as an XSS prevention technique
防止跨站点脚本 (XSS) 在 ASP.NET CorePrevent Cross-Site Scripting (XSS) in ASP.NET Core
本文内容
跨站点脚本(XSS)是一个安全漏洞,攻击者可利用此漏洞将客户端脚本(通常为 JavaScript)放入网页中。当其他用户加载受影响的页面时,攻击者的脚本将运行,使攻击者能够盗取 cookie 和会话令牌,通过 DOM 操作更改网页的内容,或者将浏览器重定向到另一页。当应用程序采用用户输入并将其输出到页面而不进行验证、编码或转义时,通常会出现 XSS 漏洞。
针对 XSS 保护应用程序Protecting your application against XSS
在基本级别,XSS 通过引诱你的应用程序将 <script>
标记插入所呈现的页中,或通过将 On*
事件插入到元素中来发挥作用。开发人员应使用以下预防步骤来避免向其应用程序引入 XSS。
请勿将不受信任的数据放入 HTML 输入,除非您按照以下步骤进行操作。不受信任的数据是指攻击者可以控制的任何数据、HTML 窗体输入、查询字符串、HTTP 标头,甚至是源自数据库的数据,即使它们不能破坏您的应用程序,也可能会破坏您的数据库。
在 HTML 元素中放置不受信任的数据之前,请确保它经过 HTML 编码。HTML 编码使用 < 等字符,并将其更改为 < 格式的安全窗体。
将不受信任的数据放入 HTML 属性之前,请确保已对其进行 HTML 编码。HTML 特性编码是 HTML 编码的超集,并对其他字符(如 "and")进行编码。
将不受信任的数据放入 JavaScript 之前,请将数据放置在运行时检索其内容的 HTML 元素中。如果无法做到这一点,请确保数据为 JavaScript 编码。JavaScript 编码会为 JavaScript 使用危险字符,并将其替换为其十六进制,例如 < 将编码为
\u003C
。将不受信任的数据置于 URL 查询字符串之前,请确保其 URL 已编码。
使用 Razor 的 HTML 编码HTML Encoding using Razor
MVC 中使用的 Razor 引擎会自动对源自变量的所有输出进行编码,除非您确实很难避免这样做。当你使用 @ 指令时,它将使用 HTML 属性编码规则。HTML 特性编码是 HTML 编码的超集,这意味着您无需担心您应该使用 HTML 编码还是 HTML 特性编码。您必须确保在 HTML 上下文中只使用 @,而不能在尝试将不受信任的输入直接插入 JavaScript 时使用。标记帮助程序还将对在标记参数中使用的输入进行编码。
获取以下 Razor 视图:
@{
var untrustedInput = "<\"123\">";
}
@untrustedInput
此视图输出untrustedInput变量的内容。此变量包含某些在 XSS 攻击中使用的字符,即 <"和 >。检查源会显示编码为的呈现输出:
<"123">
警告
ASP.NET Core MVC 提供的 HtmlString
类在输出时不会自动编码。请勿将此项与不受信任的输入结合使用,因为这将公开 XSS 漏洞。
使用 Razor 的 JavaScript 编码JavaScript Encoding using Razor
有时可能需要将值插入 JavaScript 中,以便在视图中进行处理。可通过两种方式来执行此操作。插入值的最安全方式是将值放入标记的数据属性中,并在 JavaScript 中检索它。例如:
@{
var untrustedInput = "<\"123\">";
}
<div
id="injectedData"
data-untrustedinput="@untrustedInput" />
<script>
var injectedData = document.getElementById("injectedData");
// All clients
var clientSideUntrustedInputOldStyle =
injectedData.getAttribute("data-untrustedinput");
// HTML 5 clients only
var clientSideUntrustedInputHtml5 =
injectedData.dataset.untrustedinput;
document.write(clientSideUntrustedInputOldStyle);
document.write("<br />")
document.write(clientSideUntrustedInputHtml5);
</script>
这会生成以下 HTML
<div
id="injectedData"
data-untrustedinput="<"123">" />
<script>
var injectedData = document.getElementById("injectedData");
var clientSideUntrustedInputOldStyle =
injectedData.getAttribute("data-untrustedinput");
var clientSideUntrustedInputHtml5 =
injectedData.dataset.untrustedinput;
document.write(clientSideUntrustedInputOldStyle);
document.write("<br />")
document.write(clientSideUntrustedInputHtml5);
</script>
当它运行时,将呈现以下内容:
<"123">
<"123">
还可以直接调用 JavaScript 编码器:
@using System.Text.Encodings.Web;
@inject JavaScriptEncoder encoder;
@{
var untrustedInput = "<\"123\">";
}
<script>
document.write("@encoder.Encode(untrustedInput)");
</script>
这将在浏览器中呈现,如下所示:
<script>
document.write("\u003C\u0022123\u0022\u003E");
</script>
警告
请勿连接 JavaScript 中不受信任的输入来创建 DOM 元素。应使用 createElement()
并适当地分配属性值(如 node.TextContent=
),或使用 element.SetAttribute()
/element[attribute]=
否则,你会自行向基于 DOM 的 XSS 公开。
在代码中访问编码器Accessing encoders in code
HTML、JavaScript 和 URL 编码器通过两种方式提供给你的代码,你可以通过依赖关系注入来注入它们,也可以使用 System.Text.Encodings.Web
命名空间中包含的默认编码器。如果使用默认编码器,则应用于字符范围的任何被视为安全的都不会生效-默认编码器可能会使用最安全的编码规则。
若要通过 DI 使用可配置编码器,你的构造函数应适当地采用HtmlEncoder、 JavaScriptEncoder和UrlEncoder参数。例如,
public class HomeController : Controller
{
HtmlEncoder _htmlEncoder;
JavaScriptEncoder _javaScriptEncoder;
UrlEncoder _urlEncoder;
public HomeController(HtmlEncoder htmlEncoder,
JavaScriptEncoder javascriptEncoder,
UrlEncoder urlEncoder)
{
_htmlEncoder = htmlEncoder;
_javaScriptEncoder = javascriptEncoder;
_urlEncoder = urlEncoder;
}
}
编码 URL 参数Encoding URL Parameters
如果要使用不受信任的输入生成 URL 查询字符串作为值,请使用 UrlEncoder
对值进行编码。例如,
var example = "\"Quoted Value with spaces and &\"";
var encodedValue = _urlEncoder.Encode(example);
编码后,Url-encodedvalue 变量将包含 %22Quoted%20Value%20with%20spaces%20and%20%26%22
。空格、引号、标点符号和其他不安全字符的百分比将编码为其十六进制值,例如,空格字符将变为 %20。
警告
请勿使用不受信任的输入作为 URL 路径的一部分。始终将不受信任的输入传递为查询字符串值。
自定义编码器Customizing the Encoders
默认情况下,编码器使用限制为基本拉丁语 Unicode 范围的安全列表,并将该范围之外的所有字符编码为等效的字符代码。此行为还会影响 Razor TagHelper 和 HtmlHelper 渲染,因为它将使用编码器输出字符串。
这种情况的原因是为了防止未知或将来的浏览器 bug (以前的浏览器 bug 基于非英语字符的处理来触发分析)。如果你的网站大量使用非拉丁字符(如中文、西里尔语或其他),这可能不是你所希望的行为。
在 ConfigureServices()
中,你可以自定义编码器安全列表,以包含在启动过程中适用于你的应用程序的 Unicode 范围。
例如,使用默认配置时,你可以使用 Razor HtmlHelper,如下所示;
<p>This link text is in Chinese: @Html.ActionLink("汉语/漢語", "Index")</p>
当您查看网页的源时,您将看到它的呈现方式如下:中文文本已编码;
<p>This link text is in Chinese: <a href="/">汉语/漢語</a></p>
若要放宽编码器被视为安全字符,请将以下行插入到 startup.cs
中的 ConfigureServices()
方法中:
services.AddSingleton<HtmlEncoder>(
HtmlEncoder.Create(allowedRanges: new[] { UnicodeRanges.BasicLatin,
UnicodeRanges.CjkUnifiedIdeographs }));
此示例将安全列表扩大为包含 Unicode 范围 CjkUnifiedIdeographs。呈现的输出现在变为
<p>This link text is in Chinese: <a href="/">汉语/漢語</a></p>
安全列表范围指定为 Unicode 代码图表,而不是语言。Unicode 标准包含可用来查找包含字符的图表的代码图表列表。每个编码器、Html、JavaScript 和 Url 都必须单独配置。
备注
安全列表的自定义仅影响通过 DI 的编码器。如果通过 System.Text.Encodings.Web.*Encoder.Default
直接访问编码器,则默认情况下将使用仅限基本拉丁语的唯一安全。
编码应发生在何处?Where should encoding take place?
一般接受的做法是,在输出和编码值中进行编码时,永远不应将其存储在数据库中。使用输出点编码,可以更改数据的使用,例如,从 HTML 到查询字符串值。它还使您能够轻松搜索您的数据,而无需在搜索之前对值进行编码,还可以利用对编码器进行的任何更改或 bug 修复。
验证为 XSS 保护方法Validation as an XSS prevention technique
验证是限制 XSS 攻击的有用工具。例如,仅包含字符0-9 的数字字符串将不会触发 XSS 攻击。在用户输入中接受 HTML 时,验证变得更加复杂。分析 HTML 输入很困难,如果不可能。Markdown 与带嵌入 HTML 的分析器结合使用,是接受丰富输入的一种更安全的选项。请勿仅依赖于验证。在输出之前始终对不受信任的输入进行编码,无论执行了哪些验证或清理。