8.2 History API中的新方法

8.2.1 pushState()和replaceState()

在HTML 4的History API中包含一个属性和三个方法。

HTML5的History API中新增加了两个新方法pushState()和replaceState()。这两个方法可以在不刷新页面的情况下添加和修改历史条目。

pushState()的格式如下:

pushState(data, title [, url])

pushState()的作用是在浏览器历史列表的栈顶部添加一条记录。它需要三个参数,一个是状态对象,一个是标题(这个目前被忽略),一个是URL(可选,同域)。

replacdState()的格式如下:

replaceState(data, title [, url])

replaceState()的作用是更改当前页面的历史记录。其参数和pushState()相同。

8.2.2 短地址+History新方法=完美隐藏URL恶意代码

短地址服务是指把一个冗长的网址转换成一个简洁的网址。随着Twitter等微博的兴起,短地址服务也渐渐流行起来。现在国内的微博中,都会使用短地址服务,毕竟一条微博只能输入140个字,短地址服务大大压缩了网址所占的空间。

使用bit.ly短地址服务可进行网址转换,如表8-1所示。

表8-1 短地址转换

原网址 http://hi.baidu.com/xisigr/blog/item/cb6aecc9a61ee10a7e3e6f32.html
转换后的网址 http://bit.ly/rFNVDw

不难发现,当用户单击短地址的时候,并不知道它指向哪里,此时攻击者就可以利用短地址这个特性,把注入恶意代码的网站转换为短地址,用户单击这个短地址后,就会遭到攻击。

例如,表8-2中的这个短地址转换,在恶意URL网址中,appid参数后面就是我们注入的跨站测试代码。为了隐藏URL中的跨站代码,可以使用短地址服务http://bit.ly/将其转换为短地址,转换后的短地址为http://bit.ly/qxJuSF。此时转换后的短地址已经看不出来是一个恶意网址了。

表8-2 恶意短地址转换

恶意URL网址 http://mail.test.com/?userid=&appid=<script>document.write(1)</script>
转换后的网址 http://bit.ly/qxJuSF

此时,你也许会认为这样的隐藏太完美了。其实它也存在不足,当用户单击http://bit.ly/qxJuSF后,在浏览器的URL地址栏中仍会呈现http://mail.test.com/?userid=&appid=<script> document. write(1)</script>地址。只能说使用短地址服务隐藏恶意代码只隐藏了一半,用户始终可以看到注入的恶意代码。

接下来使用History的新方法来隐藏另一半,让恶意代码执行后,用户无法看到URL中的恶意代码。要说明的是,修改后的URL必须和当前的URL同域,这里把URL参数的位置写成location.href.split("?").shift()。

短地址转换前后如表8-3或表8-4所示。

表8-3 恶意短地址转换隐藏技巧1

恶意URL网址 http://mail.test.com/?userid=&appid=<script>history.pushState({},'',
location.href.split("?").shift());document.write(1)</script>
转换后的网址 http://bit.ly/uYAqsC

表8-4 恶意短地址转换隐藏技巧2

恶意URL网址 http://mail.test.com/?userid=&appid=<script>history.replaceState({},'',
location.href.split("?").shift());document.write(1)</script>
转换后的网址 http://bit.ly/tKJvnY

当用户单击短地址链接后,看到浏览器中的URL将会被替换为http://www.mail.test.com/。参数appid后面的跨站代码document.write(1)此时也已经执行。

通过上面的分析知道,传统方式下隐藏URL中的恶意代码可以使用短地址服务。但这样隐藏后,当用户单击执行时,还是会在浏览器URL地址栏中看到恶意代码。现在可以利用History的新方法pushState()和replaceState(),在无刷新页面的情况下改变地址栏中的URL,用户就无法看到恶意代码。所以,通过以上两种方法的结合,可以完美地隐藏URL中的恶意代码。

8.2.3 伪造历史记录

在Chrome浏览器中打开以下脚本程序:

<script>
for(i=0;i<=100;i++){history.pushState({},"","/"+i+".html");
</script>

然后在Chrome浏览器中查看历史记录,可以看到有很多历史记录信息,如图8-1所示,而这些链接信息用户其实根本没有访问过。

303-1

图8-1 Chrome下伪造历史记录

通过这个例子我们可以看到,使用history.pushState可以对浏览器的历史记录进行伪造,而且也可以对历史记录发起DoS攻击。