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.
View live demo Download the source files
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("https://ryanseddon.com/");
},);
}
}, 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("https://ryanseddon.com/");
},);
}
}, 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!