HTML与JavaScript自解码机制
关于这个自解码机制,我们直接以一个例子(样例0)来进行说明:
<input type="button" id="exec_btn" value="exec" onclick="document.write ('<img src=@ onerror=alert(123) />')" />
我们假设
document.write
里的值是用户可控的输入,点击后,
document.write
出现一段
img HTML
,
onerror
里的 JavaScript 会执行。此时陷阱来了,我们现在提供一段
HtmlEncode
函数如下(样例A):
<script>
function HtmlEncode(str) {
var s = "";
if (str.length == 0) return "";
s = str.replace(/&/g, "&");
s = s.replace(/</g, "<");
s = s.replace(/>/g, ">");
s = s.replace(/\"/g, """);
return s;
</script>
<input type="button" id="exec_btn" value="exec" onclick="document.write (HtmlEncode('<img src=@ onerror=alert(123) />'))" />
我们知道
HtmlEncode('<img src=@ onerror=alert(123) />')
后的结果是:
<img src=@ onerror=alert(123) />
这个样例 A 点击后会执行
alert(123)
吗?下面这个呢(样例 B)?
<input type="button" id="exec_btn" value="exec" onclick="document.write ('<img src=@ onerror=alert(123) />')" />
在样例 A 和样例 B 中,
document.write
的值似乎是一样的?实际结果是样例 A 点击不会执行
alert(123)
,而是在页面上完整地输出
<img src=@ onerror=alert(123) />
,而样例 B 点击后会执行
alert(123)
。
我们要告诉大家的是,点击样例B时,
document.write
的值实际上不再是:
<img src=@ onerror=alert(123) />
而是
<img src=@ onerror=alert(123) />
我们可以这样论证:
<input type="button" id="exec_btn" value="exec" onclick="x='<img src=@ onerror=alert(123) />';alert(x);document.write(x)" />
看弹出的x值就知道了,如下图所示。
出现这个结果的原因如下:
onclick
里的这段 JavaScript 出现在 HTML 标签内,意味着这里的JavaScript 可以进行 HTML 形式的编码,这种编码有以下两种。
-
进制编码:
&#xH;
(十六进制格式)、&#D;
(十进制格式),最后的分号(;)可以不要。 -
HTML 实体编码:即上面的那个
HtmlEncode
。
在 JavaScript 执行之前,HTML 形式的编码会自动解码。所以样例 0 与样例 B 的意义是一样的,而样例 A 就不一样了。下面我们继续完善这些例子。
上面的用户输入是出现在 HTML 里的情况,如果用户输入出现在
<script>
里的 JavaScript 中,情况会怎样,代码如下:
<input type="button" id="exec_btn" value="exec" />
<script>
function $(id){return document.getElementById(id);};
$('exec_btn').onclick = function(){
document.write("<img src=@ onerror=alert(123) />");
//document.write("<img src=@ onerror=alert(123) />");