关于这个自解码机制,我们直接以一个例子(样例0)来进行说明:
|
|
我们假设document.write
里的值是用户可控的输入,点击后,document.write
出现一段 img HTML
,onerror
里的 JavaScript 会执行。此时陷阱来了,我们现在提供一段HtmlEncode
函数如下(样例A):
我们知道HtmlEncode('<img src=@ onerror=alert(123) />')
后的结果是:
<img src=@ onerror=alert(123) />
这个样例 A 点击后会执行 alert(123)
吗?下面这个呢(样例 B)?
在样例 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) />
我们可以这样论证:
看弹出的x值就知道了,如下图所示。
出现这个结果的原因如下:
onclick
里的这段 JavaScript 出现在 HTML 标签内,意味着这里的JavaScript 可以进行 HTML 形式的编码,这种编码有以下两种。
- 进制编码:
&#xH;
(十六进制格式)、&#D;
(十进制格式),最后的分号(;)可以不要。- HTML 实体编码:即上面的那个
HtmlEncode
。
在 JavaScript 执行之前,HTML 形式的编码会自动解码。所以样例 0 与样例 B 的意义是一样的,而样例 A 就不一样了。下面我们继续完善这些例子。
上面的用户输入是出现在 HTML 里的情况,如果用户输入出现在<script>
里的 JavaScript 中,情况会怎样,代码如下:
这样是可以执行alert(123)
的,如果用户输入的是下面的内容:<img src=@ onerror=alert(123) />
结果与样例 B 一样:这段 HTML 编码的内容在 JavaScript 执行之前自动解码吗?答案是不会,原因是用户输入的这段内容上下文环境是 JavaScript,不是HTML(可以认为<script>
标签里的内容和 HTML 环境毫无关系),此时用户输入的这段内容要遵守的是 JavaScript 法则,即 JavaScript 编码,具体有如下几种形式。
- Unicode 形式:\uH (十六进制)。
- 普通十六进制:\xH。
- 纯转义: \’ 、 \” 、 \< 、 > 这样在特殊字符之前加 \ 进行转义。
比如,用户输入被转义成如下形式:\<img src\=@ onerror=alert\(123\) \/\>
在 JavaScript 执行之前,这样的转义会自动去转义,alert(123)
照样执行。同样,下面这样的 JavaScript 编码也毫无意义:
在 JavaScript 执行之前,这样的编码会自动解码。
通过这几个样例,我们可以知道在 HTML 中与在 JavaScript 中自动解码的差异。
参考《Web 前端黑客技术揭秘》