Stealing the users back button with the History API

Gruber posted a video of a website that does some dodgy history insertion. Go to tgdaily.com let it load (it has horrible perf so give it a bit) and click back and you’ll notice that you get taken back to exitjunction.com with tgdaily as a query. Insert rage face here. Once past rage face open dev tools and investigate.

TL;DR

They’re using the history API to initially replace your state then push the original unaltered state back so it looks like you went nowhere. Clicking back takes you to that special state which then does a location.replace to your new back state, because that’s how you win readers. See demo below for simplified version.

history.replaceState(null, document.title, location.pathname+"#!/stealingyourhistory");
history.pushState(null, document.title, location.pathname);

window.addEventListener("popstate", function() {
  if(location.hash === "#!/stealingyourhistory") {
	history.replaceState(null, document.title, location.pathname);
	setTimeout(function(){
	  location.replace("http://www.thecssninja.com/");
	},0);
  }
}, false);

Very simplified version they also cover non history API supporting browsers by doing hashchange and polling failing that. But I only have eyes for history API.

history.replaceState(null, document.title, location.pathname+"#!/stealingyourhistory");
history.pushState(null, document.title, location.pathname);

Upon loading the demo I replace the current URL with a #!/stealingyourhistory then sneakily push a new state on top for the original unaltered domain.

window.addEventListener("popstate", function() {
  if(location.hash === "#!/stealingyourhistory") {
	setTimeout(function(){
	  location.replace("http://www.thecssninja.com/");
	},0);
  }
}, false);

Attaching to the “popstate” event so we know when the user is going back I check the location.hash for our dodgy one and then replace the location to whatever dodgy website I want you to go back too.

The zero setTimeout is so we’ll be placed to run at the end of the browser event loop.

It’s more noticeable in some browsers

Thankfully Firefox and Chrome pause for a moment after a popstate so you can see the dodgy inserted hash url before the page redirects to your new history state. Opera and Safari will do it instantaneously :S.

However we can “improve” their script by doing another sneaky line of code.

history.replaceState(null, document.title, location.pathname);

All we’ve done is once again use a replaceState to restore the url to the clean unaltered state so all that happens is a slight pause in Chrome and Firefox and no URL change to the user.

Don’t do this!

[link href=”http://cssn.in/ja/047″]