首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >XSS预防和.innerHTML

XSS预防和.innerHTML
EN

Stack Overflow用户
提问于 2015-06-05 16:08:49
回答 3查看 27K关注 0票数 14

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

代码语言:javascript
复制
element.innerHTML = “User provided variable”;

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

代码语言:javascript
复制
<img src=a onerror='alert();'>

只使用超文本标记语言或者只使用JS编码是没有帮助的,因为我所理解的.innerHTML方法在将输入插入页面之前先对其进行解码。使用HTML+JS编码时,我注意到.innerHTML只解码JS,但仍然保留HTML编码。

但是我可以通过双重编码到HTML中来实现同样的效果。

我的问题是:谁能提供一个例子,说明为什么在使用.innerHTML方法时,我应该先进行HTML编码,然后再进行JS编码,而不是在HTML中进行双重编码?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-06-08 19:20:47

谁能提供一个例子,说明为什么在使用.innerHTML方法时,我应该先进行HTML编码,然后再进行JS编码,而不是在HTML中进行双重编码?

好的。

假设“用户提供的数据”是由服务器填充到您的JavaScript中的,那么您将不得不进行JS编码才能将其放入其中。

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

代码语言:javascript
复制
var userProdividedData = "<%=serverVariableSetByUser %>";
element.innerHTML = userProdividedData;

与ASP.NET一样,<%= %>在不编码的情况下输出服务器端变量。如果用户是"good“,并且提供了值foo,则会呈现以下JavaScript:

代码语言:javascript
复制
var userProdividedData = "foo";
element.innerHTML = userProdividedData;

到目前为止还没有问题。

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

代码语言:javascript
复制
var userProdividedData = ""; alert("xss attack!");//";
element.innerHTML = userProdividedData;

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

为了防止这种情况,正如您所说的,您可以进行JS编码。OWASP XSS prevention cheat sheet rule #3说:

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

因此,为了防止出现这种情况,您的代码应该是

代码语言:javascript
复制
var userProdividedData = "<%=JsEncode(serverVariableSetByUser) %>";
element.innerHTML = userProdividedData;

其中JsEncode按照OWASP建议进行编码。

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

代码语言:javascript
复制
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')" />作为值,该怎么办?这对于变量赋值部分来说是很好的,因为它将被简单地转换成与上面一样的十六进制实体。

不过,这条线

代码语言:javascript
复制
element.innerHTML = userProdividedData;

将导致在浏览器呈现内部超文本标记语言时执行alert('xss attack')。这类似于DOM Based XSS攻击,因为它使用呈现的JavaScript而不是HTML.然而,当它通过服务器时,它仍然被归类为反射的或存储的XSS,这取决于该值的初始设置位置。

这就是为什么你也需要HTML编码。这可以通过如下函数来完成:

代码语言:javascript
复制
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;')
}

编写你的代码

代码语言:javascript
复制
element.innerHTML = escapeHTML(userProdividedData);

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

更新关于注释中的问题

我还有一个问题:你提到我们必须进行JS编码,因为攻击者可能会进入"; alert("xss attack!");//。但是如果我们使用超文本标记语言编码而不是JS编码,那么超文本标记语言不也会对"符号进行编码,从而使这种攻击变得不可能吗,因为我们将拥有:var userProdividedData ="&quot;; alert(&quot;xss attack!&quot;);&#x2F;&#x2F;";

我将你的问题理解为以下意思:与其JS编码,然后HTML编码,为什么我们不从一开始就只使用HTML编码,并将其留在那里?

因为它们可以对攻击进行编码,例如<img src="xx" onerror="alert('xss attack')" />,所有这些攻击都使用\xHH格式编码以插入其有效载荷-这将实现所需的攻击的超文本标记语言序列,而不使用超文本标记语言编码将影响的任何字符。

还有一些其他的攻击:如果攻击者输入了\,那么他们可以强迫浏览器错过右引号(因为\是JavaScript中的转义符)。

这将呈现为:

代码语言:javascript
复制
var userProdividedData = "\";

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

另外,假设有两条用户控制的数据:

代码语言:javascript
复制
var userProdividedData = "<%=serverVariableSetByUser1 %>" + ' - ' + "<%=serverVariableSetByUser2 %>";

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

代码语言:javascript
复制
var userProdividedData = "\" + ' - ' + ";alert('xss');//";

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

代码语言:javascript
复制
<a href="javascript:void(0)" onclick="myFunction('<%=JsEncode(serverVariableSetByUser) %>'); return false">

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

代码语言:javascript
复制
<a href='javascript:void(0)' onclick='myFunction("<%=JsEncode(serverVariableSetByUser) %>"); return false'>

甚至不加引号:

代码语言:javascript
复制
<a href=javascript:void(0) onclick=myFunction("<%=JsEncode(serverVariableSetByUser) %>");return false;>

如果您像注释中提到的那样对实体值进行了HTML编码:

onclick='var userProdividedData ="&quot;;"' (简写版本)

代码实际上是首先通过浏览器的HTML解析器运行的,所以userProdividedData应该是

代码语言:javascript
复制
";;

而不是

代码语言:javascript
复制
&quot;;

因此,当您将其添加到innerHTML调用中时,您将再次拥有XSS。请注意,除了结束<script>标记but,即another story之外,不会通过浏览器的HTML解析器处理HTML块。

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

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

代码语言:javascript
复制
alert(serverVariableSetByUser);

就像设置HTML一样简单

代码语言:javascript
复制
element.innerHTML = escapeHTML(userProdividedData);

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

票数 24
EN

Stack Overflow用户

发布于 2015-06-06 07:35:19

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

代码语言:javascript
复制
element.textContent = "User provided variable with <img src=a>";

另一种选择是仅在对要使用的值进行编码(如果有机会,最好是在服务器上)之后才使用innerHTML

票数 8
EN

Stack Overflow用户

发布于 2016-12-19 18:06:39

我在我的ASP.NET Webforms应用程序中遇到过这个问题。解决这个问题的方法相对简单。

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

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

代码语言:javascript
复制
YourHtmlElement.InnerHtml = "Your HTML content" ;

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

代码语言:javascript
复制
string unsafeHtml = "Your HTML content"; 
YourHtmlElement.InnerHtml = Sanitizer.GetSafeHtml(unsafeHtml);

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30661497

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档