当我允许用户将数据作为参数插入JS innerHTML
函数时,如下所示:
element.innerHTML = “User provided variable”;
我明白为了防止XSS,我必须进行HTML编码,然后对用户输入进行JS编码,因为用户可以插入这样的内容:
<img src=a onerror='alert();'>
只有HTML或仅JS编码无济于事,因为我理解的.innerHTML
方法在将输入插入页面之前解码输入。使用HTML + JS编码,我注意到.innerHTML
仅解码JS,但HTML编码仍然存在。
但我能够通过双重编码实现相同的目标。
我的问题是:有人可以举例说明为什么我应该进行HTML编码然后进行JS编码,而不是在使用.innerHTML
方法时在HTML中进行双重编码?
答案 0 :(得分:17)
有人可以举例说明为什么我应该进行HTML编码 使用.innerHTML时,JS编码,而不是HTML编码 方法
不确定
假设&#34;用户提供了数据&#34;服务器在您的JavaScript中填充,然后您必须使用JS编码才能获得它。
以下是服务器端的伪代码,但前端是JavaScript:
var userProdividedData = "<%=serverVariableSetByUser %>";
element.innerHTML = userProdividedData;
与ASP.NET <%= %>
一样,输出服务器端变量而不进行编码。如果用户是好的&#34;并提供值foo
,然后这会导致呈现以下JavaScript:
var userProdividedData = "foo";
element.innerHTML = userProdividedData;
到目前为止没有任何问题。
现在说恶意用户提供值"; alert("xss attack!");//
。这将呈现为:
var userProdividedData = ""; alert("xss attack!");//";
element.innerHTML = userProdividedData;
这将导致XSS漏洞,其中代码实际上是在上面的第一行中执行的。
为了防止这种情况,正如你所说的JS编码。 OWASP XSS prevention cheat sheet rule #3说:
除字母数字字符外,请将所有字符转义为小于 256用\ xHH格式防止切换出数据值 进入脚本上下文或另一个属性。
因此,为了防止这种情况,您的代码将是
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 Based XSS攻击。
这就是你需要HTML编码的原因。这可以通过以下功能完成:
function escapeHTML (unsafe_str) {
return unsafe_str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\"/g, '"')
.replace(/\'/g, ''')
.replace(/\//g, '/')
}
制作代码
element.innerHTML = escapeHTML(userProdividedData);
或者可以通过JQuery&#39; text()
函数完成。
我还有一个问题:你提到我们必须JS编码 因为攻击者可以进入
"; alert("xss attack!");//
。但是,如果我们 会使用HTML编码而不是JS编码,也不会 HTML会对"
符号进行编码,因此我们无法进行此攻击 会:var userProdividedData =""; alert("xss attack!");//";
我提出的问题意味着以下内容:为什么我们不首先使用HTML编码,而不是使用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 ="";"'
(缩短版)
代码实际上是先通过浏览器的HTML解析器运行,因此userProdividedData
将是
";;
而不是
";
因此当您将其添加到innerHTML
电话时,您将再次使用XSS。请注意,<script>
块不会通过浏览器的HTML解析器进行处理,但结束</script>
代码but除了another story之外。
如上所示,编码为 late 总是明智的。然后,如果您需要在JavaScript上下文之外的任何内容中输出值(例如,实际的警告框不呈现HTML,那么它仍将正确显示)。
也就是说,上面我可以打电话
alert(serverVariableSetByUser);
和设置HTML一样简单
element.innerHTML = escapeHTML(userProdividedData);
在这两种情况下,它都会正确显示而不会破坏输出或导致不良代码执行。
答案 1 :(得分:6)
确保element
内容正确编码(并且不会被解析为HTML)的简单方法是使用textContent
代替innerHTML
:
element.textContent = "User provided variable with <img src=a>";
另一种选择是仅在您编码后使用innerHTML
(如果有机会,最好在服务器上)使用您想要使用的值。
答案 2 :(得分:2)
我在ASP.NET Webforms应用程序中遇到过这个问题。对此的修复相对简单。
从NuGet Package Manager安装HtmlSanitizationLibrary并在您的应用程序中引用它。在后面的代码中,请按以下方式使用清洁剂类。
例如,如果当前代码看起来像这样,
YourHtmlElement.InnerHtml = "Your HTML content" ;
然后,将其替换为以下内容:
string unsafeHtml = "Your HTML content";
YourHtmlElement.InnerHtml = Sanitizer.GetSafeHtml(unsafeHtml);
此修复程序将删除Veracode漏洞并确保该字符串呈现为HTML。在后面的代码中对字符串进行编码会将其呈现为“未编码的字符串”而不是RAW HTML,因为它在渲染开始之前进行了编码。