繁体   English   中英

XSS 预防和 .innerHTML

[英]XSS prevention and .innerHTML

当我允许用户将数据作为参数插入到 JS innerHTML函数时,如下所示:

element.innerHTML = “User provided variable”;

我知道为了防止 XSS,我必须对用户输入进行 HTML 编码,然后 JS 对用户输入进行编码,因为用户可以插入这样的内容:

<img src=a onerror='alert();'>

只有 HTML 或只有 JS 编码无济于事,因为我理解的.innerHTML方法在将输入插入页面之前对其进行解码。 使用 HTML+JS 编码,我注意到.innerHTML只解码 JS,但 HTML 编码仍然存在。

但是我能够通过双重编码到 HTML 来实现相同的目标。

我的问题是:有人可以提供一个示例,说明为什么我应该在使用.innerHTML方法时对 HTML 进行编码然后进行 JS 编码,而不是在 HTML 中进行双重编码?

有人可以提供一个示例,说明为什么我应该在使用 .innerHTML 方法时进行 HTML 编码然后 JS 编码,而不是在 HTML 中进行双重编码?

当然。

假设服务器在您的 JavaScript 中填充了“用户提供的数据”,那么您将必须对它进行 JS 编码才能获得它。

以下是服务器端的伪代码,但前端是 JavaScript:

var userProdividedData = "<%=serverVariableSetByUser %>";
element.innerHTML = userProdividedData;

像 ASP.NET <%= %>输出没有编码的服务器端变量。 如果用户“好”并提供值foo那么这将导致以下 JavaScript 被呈现:

var userProdividedData = "foo";
element.innerHTML = userProdividedData;

到目前为止没有问题。

现在假设恶意用户提供值"; alert("xss attack!");// 。这将呈现为:

var userProdividedData = ""; alert("xss attack!");//";
element.innerHTML = userProdividedData;

这将导致 XSS 漏洞利用,其中代码实际上在上面的第一行中执行。

为了防止这种情况,正如您所说,您进行了 JS 编码。 OWASP XSS 预防备忘单规则 #3说:

除字母数字字符外,使用 \\xHH 格式对所有小于 256 的字符进行转义,以防止将数据值切换到脚本上下文或另一个属性中。

因此,为了防止这种情况,您的代码将是

var userProdividedData = "<%=JsEncode(serverVariableSetByUser) %>";
element.innerHTML = userProdividedData;

JsEncode根据 OWASP 建议进行编码。

这将防止上述攻击,因为它现在呈现如下:

var userProdividedData = "\x22\x3b\x20alert\x28\x22xss\x20attack\x21\x22\x29\x3b\x2f\x2f";
element.innerHTML = userProdividedData;

现在您已经针对 XSS 保护了您的 JavaScript 变量分配。

但是,如果恶意用户提供<img src="xx" onerror="alert('xss attack')" />作为值呢? 这对于变量赋值部分很好,因为它会像上面一样简单地转换为等效的十六进制实体。

然而线

element.innerHTML = userProdividedData;

当浏览器呈现内部 HTML 时,会导致alert('xss attack')被执行。 这类似于基于DOM 的 XSS攻击,因为它使用呈现的 JavaScript 而不是 HTML,然而,当它通过服务器时,它仍然被归类为反射型或存储型 XSS,具体取决于值的初始设置位置。

这就是为什么您也需要进行 HTML 编码的原因。 这可以通过一个函数来完成,例如:

function escapeHTML (unsafe_str) {
    return unsafe_str
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/\"/g, '&quot;')
      .replace(/\'/g, '&#39;')
      .replace(/\//g, '&#x2F;')
}

制作你的代码

element.innerHTML = escapeHTML(userProdividedData);

或者可以通过 JQuery 的text()函数来完成。

关于评论中问题的更新

我还有一个问题:您提到我们必须进行 JS 编码,因为攻击者可以输入"; alert("xss attack!");// 。但是如果我们使用 HTML 编码而不是 JS 编码,那岂不是也HTML 对"符号进行编码并使这种攻击成为不可能,因为我们将有: var userProdividedData ="&quot;; alert(&quot;xss attack!&quot;);&#x2F;&#x2F;";

我认为您的问题意味着以下内容:与其先进行 JS 编码,然后再进行 HTML 编码,为什么我们不首先进行 HTML 编码,然后就这样呢?

好吧,因为他们可以对诸如<img src="xx" onerror="alert('xss attack')" />编码,所有这些都使用\\xHH格式进行编码以插入其有效载荷 - 这将实现所需的 HTML 序列在不使用 HTML 编码会影响的任何字符的情况下进行攻击。

还有一些其他的攻击:如果攻击者输入\\那么他们可能会强制浏览器错过结束引号(因为\\是 JavaScript 中的转义字符)。

这将呈现为:

var userProdividedData = "\";

这会触发 JavaScript 错误,因为它不是一个正确终止的语句。 如果应用程序在显眼位置呈现,这可能会导致应用程序拒绝服务。

另外说有两个用户控制的数据:

var userProdividedData = "<%=serverVariableSetByUser1 %>" + ' - ' + "<%=serverVariableSetByUser2 %>";

然后,用户可以在第一个中输入\\ ,在第二个中输入;alert('xss');// 这会将字符串连接更改为一个大任务,然后是 XSS 攻击:

var userProdividedData = "\" + ' - ' + ";alert('xss');//";

由于像这样的边缘情况,建议遵循 OWASP 指南,因为它们尽可能接近防弹。 您可能认为在 HTML 编码值列表中添加\\可以解决这个问题,但是在以这种方式呈现内容时使用 JS 后跟 HTML 还有其他原因,因为此方法也适用于属性值中的数据:

<a href="javascript:void(0)" onclick="myFunction('<%=JsEncode(serverVariableSetByUser) %>'); return false">

不管是单引号还是双引号:

<a href='javascript:void(0)' onclick='myFunction("<%=JsEncode(serverVariableSetByUser) %>"); return false'>

甚至不加引号:

<a href=javascript:void(0) onclick=myFunction("<%=JsEncode(serverVariableSetByUser) %>");return false;>

如果您按照评论中提到的方式进行 HTML 编码,则为实体值:

onclick='var userProdividedData ="&quot;;"' (缩短版)

代码实际上首先通过浏览器的 HTML 解析器运行,因此userProdividedData将是

";;

代替

&quot;;

因此,当您将其添加到innerHTML调用时,您将再次遇到 XSS。 请注意,除了结束</script>标记外, <script>块不会通过浏览器的 HTML 解析器进行处理,但这另一回事

如上所示,尽可能地编码总是明智的。 然后,如果您需要在 JavaScript 上下文之外的任何内容中输出值(例如,实际的警报框不呈现 HTML,那么它仍将正确显示)。

也就是说,有了上面我可以调用

alert(serverVariableSetByUser);

就像设置 HTML 一样简单

element.innerHTML = escapeHTML(userProdividedData);

在这两种情况下,它都会正确显示,而不会中断输出或导致不良代码执行的某些字符。

确保element内容正确编码(并且不会被解析为 HTML)的一种简单方法是使用textContent而不是innerHTML

element.textContent = "User provided variable with <img src=a>";

另一种选择是仅在您编码(如果有机会,最好在服务器上)您打算使用的值后才使用innerHTML

我在我的 ASP.NET Webforms 应用程序中遇到过这个问题。 对此的修复相对简单。

从 NuGet 包管理器安装 HtmlSanitizationLibrary 并在您的应用程序中引用它。 在后面的代码中,请按以下方式使用 sanitizer 类。

例如,如果当前代码看起来像这样,

YourHtmlElement.InnerHtml = "Your HTML content" ;

然后,将其替换为以下内容:

string unsafeHtml = "Your HTML content"; 
YourHtmlElement.InnerHtml = Sanitizer.GetSafeHtml(unsafeHtml);

此修复程序将消除 Veracode 漏洞并确保将字符串呈现为 HTML。 在代码后面对字符串进行编码会将其呈现为“未编码的字符串”而不是 RAW HTML,因为它是在呈现开始之前进行编码的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM