XSS预防和.innerHTML

sta*_*nko 9 javascript xss encoding innerhtml

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

element.innerHTML = “User provided variable”;
Run Code Online (Sandbox Code Playgroud)

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

<img src=a onerror='alert();'>
Run Code Online (Sandbox Code Playgroud)

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

但我能够通过双重编码实现相同的HTML.

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

Sil*_*Fox 18

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

当然.

假设服务器在您的JavaScript中填充了"用户提供的数据",那么您必须使用JS编码才能获得它.

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

var userProdividedData = "<%=serverVariableSetByUser %>";
element.innerHTML = userProdividedData;
Run Code Online (Sandbox Code Playgroud)

像ASP.NET一样<%= %>输出没有编码的服务器端变量.如果用户"良好"并提供该值,foo则会导致呈现以下JavaScript:

var userProdividedData = "foo";
element.innerHTML = userProdividedData;
Run Code Online (Sandbox Code Playgroud)

到目前为止没有问题.

现在说恶意用户提供该值"; alert("xss attack!");//.这将呈现为:

var userProdividedData = ""; alert("xss attack!");//";
element.innerHTML = userProdividedData;
Run Code Online (Sandbox Code Playgroud)

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

为了防止这种情况,正如你所说的JS编码.在OWASP XSS预防小抄规则#3说:

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

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

var userProdividedData = "<%=JsEncode(serverVariableSetByUser) %>";
element.innerHTML = userProdividedData;
Run Code Online (Sandbox Code Playgroud)

JsEncode根据OWASP建议进行编码的地方.

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

var userProdividedData = "\x22\x3b\x20alert\x28\x22xss\x20attack\x21\x22\x29\x3b\x2f\x2f";
element.innerHTML = userProdividedData;
Run Code Online (Sandbox Code Playgroud)

现在,您已经针对XSS保护了JavaScript变量赋值.

但是,如果恶意用户提供<img src="xx" onerror="alert('xss attack')" />的价值怎么办?这对于变量赋值部分来说没问题,因为它将简单地转换为与上面相同的十六进制实体.

但行

element.innerHTML = userProdividedData;
Run Code Online (Sandbox Code Playgroud)

将导致alert('xss attack')在浏览器呈现内部HTML时执行.这将是基于DOM的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;')
}
Run Code Online (Sandbox Code Playgroud)

制作你的代码

element.innerHTML = escapeHTML(userProdividedData);
Run Code Online (Sandbox Code Playgroud)

或者可以通过JQuery的text()功能完成.

关于评论中的问题的更新

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

我的问题意味着以下内容:我们为什么不首先编写HTML编码,而不是JS编码,而不是HTML编码?

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

还有一些其他的攻击:如果攻击者进入\那么他们可能会强制浏览器错过结束引用(就像\JavaScript中的转义字符一样).

这将呈现为:

var userProdividedData = "\";
Run Code Online (Sandbox Code Playgroud)

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

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

var userProdividedData = "<%=serverVariableSetByUser1 %>" + ' - ' + "<%=serverVariableSetByUser2 %>";
Run Code Online (Sandbox Code Playgroud)

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

var userProdividedData = "\" + ' - ' + ";alert('xss');//";
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

甚至没有引用:

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

如果您在评论中提到的HTML编码实体值:

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

代码实际上是先通过浏览器的HTML解析器运行,所以userProdividedData也是如此

";;
Run Code Online (Sandbox Code Playgroud)

代替

&quot;;
Run Code Online (Sandbox Code Playgroud)

因此,当您将其添加到innerHTML呼叫时,您将再次使用XSS.请注意<script>,除了结束</script>标记之外,不会通过浏览器的HTML解析器处理块,但这另一个故事.

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

也就是说,通过以上我可以打电话

alert(serverVariableSetByUser);
Run Code Online (Sandbox Code Playgroud)

就像设置HTML一样容易

element.innerHTML = escapeHTML(userProdividedData);
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,它都会正确显示,而不会破坏输出或导致不良代码执行.


小智 7

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

element.textContent = "User provided variable with <img src=a>";
Run Code Online (Sandbox Code Playgroud)

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