Mastering The Back Button With JavascriptFiled: Wed, Feb 28 2007 under Programming|| Tags: ajax history secure sessions Ask how to control the back button on a forum and you'll be quickly lectured how interfering with the browser's history is evil and that you're a bad person for wanting to do it. You'll then be told it's impossible. But, guess what? Both groups are wrong! Almost everyone who asks how to control the back button is trying to make pages accessed while a user was logged in, inaccessible once a user has logged out. A good example of this is a banking page. If you log in, transfer a few funds, then log out, no one should be able to hit the back button a few time and see what you were doing. So wanting to control the back button isn't an evil request. There are many, MANY legitimate uses for wanting to do so. window.onbeforeunloadAround Internet Explorer version 4.0, Microsoft decided there needed to be
a way for secure banking and e-commerce sites to have some control over the
session history. They did this with the javascript event There are two things that happen when you set an onbeforeunload event. One, you can give the user a chance to re-think the decision to leave the page by spawning a "yes/no" dialog box; and two, just having an onbeforeunload event will cause the browser to never cache the page: This means that the page will always be rendered exactly as if the user was hitting it for the very first time.
window.onbeforeunload = function () {
// stuff do do before the window is unloaded here.
}
Are you really, really sure you want to leave my glorious page?What makes Are you sure you want to navigate away from this page? Click OK to continue, or Cancel to stay on the current page. This dialog box will appear if you return ANYTHING in your function. return null, return false, return true, return "some text" will all bring up the confirmation dialog box. You can insert your own explanatory text BETWEEN those two lines by including a string on the return statement. For instance:
window.onbeforeunload = function () {
return "You have not saved your document yet. If you continue, your work will not be saved."
}
…will change the alert box to read... Are you sure you want to navigate away from this page? You have not saved your document yet. If you continue, your work will not be saved. Click OK to continue, or Cancel to stay on the current page. As you can see, the starting and ending lines stay the same, your text is inserted into the middle. If the user clicks ok, the onunload event will trigger and the browser will navigate to the new page. If the user clicks cancel the user will be returned to the current page where he or she left off. You can see an example of this by clicking this link. If you click OK this timestamp should change (). If you click cancel, the timestamp should remain the same. If you don't want a confirmation dialog box, simply do not include a return statement in your function. Just close the curly braces ( } ) naturally without a return statement.
window.onbeforeunload = function () {
// This fucntion does nothing. It won't spawn a confirmation dialog
// But it will ensure that the page is not cached by the browser.
}
Detecting When The User Has Clicked CancelOne of the things you may want to do is to be notified when the user clicks
cancel, aborting a page unload. Unfortunately there's no way to be immediately
notified. The best you can do is to set a unique global variable in your The example code I used above to do an example of an
var _isset=0;
function demo() {
window.onbeforeunload = function () {
if (_isset==0) {
_isset=1; // This will only be seen elsewhere if the user cancels.
return "This is a demonstration, you won't leave the page whichever option you select.";
}
}
_isset=0;
window.location.reload();
return false;
}
This code defines a global variabled named _isset, and then initializes it to zero.
In our But as you can see this method won't help you if you need to be immediately notified that that the user has finished dealing with the confirmation box. You can detect when it appears on the screen but there's no way to know when the user has finished interacting with it if the user clicked cancel (if the user clicked OK, then of course the unload event will have been tripped). Truly Dynamic PagesJust having an unbeforeunload event handler -- regardless of whether or not it actually does anything, regardless of whether or not you spawn a dialog box or not, even if the entire function declaration consists entirely of just { } -- just defining an event handler will prevent the page from being cached -- ever. As a matter of fact, even if you allow page caching, the page will be not be
cached. Having an Some security considerations.Although
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Pragma: no-cache");
And with browser meta tags (not as reliable but it never hurts to try). <META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE"> <META HTTP-EQUIV="EXPIRES" CONTENT="01 Jan 1970 00:00:00 GMT"> <META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE"> Now in the Good cache-control will reduce the chance that someone can go through the physical cache on the user's machine and extract sensitive information. Logged out? No page for you!Since Shutdown NicelyIn addition to disabling page caching,
window.onbeforeunload = function() {
// This template uses no error checking, it's just concept code to be
// expanded on.
// Create a new XMLHttpRequest object
var AJAX=new XMLHttpRequest();
// Handle ready state changes ( ignore them until readyState = 4 )
AJAX.onreadystatechange= function() { if (AJAX.readyState!=4) return false; }
// we're passing false so this is a syncronous request.
// The script will stall until the document has been loaded.
// the open statement depends on a global variable titled _userID.
AJAX.open("GET", 'http://someurl.com/endsession.php?id='+_userID, false);
AJAX.send(null);
}
The choice to use a synchronous call is deliberate and important. A synchronous request will stall the browser until it gets a reply back from the server. If we were to initiate an asynchronous request the request would be made but the browser would continue the unload event and chances are the browser would never get the response back from the server. So in your unload events you need to keep AJAX synchronous. Browser Check
You can't change historyThe history object, for good reason, has been put into a security lockdown. You can't use it to modify the history. You can however have some small and very imperfect control over the current url. location.replace(url) Using the command above you can replace the current page with the url of your
choice, this replaces the current URL in the user's history as well. If
the page is VERY sensitive you can use this when the user opts to log out.
Firefox will let you use location.replace(URL) right before the return statement
in an
window.onbeforeunload = function () {
location.replace('http://www.google.com');
return "This session is expired and the history altered.";
}
It's kind of clunky in that if the user opts to cancel he'll be on the replacement
page instead of the original page, and the replacement page will actually be loaded
as he ponders the confirmation dialog. If he selects It's important to note than in Internet Explorer 6 that you can not use a location method at all in any unload event, so the trick of modifying the history of the current page will work (kinda) only in Firefox. It's not something you can depend on. If you MUST control HistoryIf you absolutely must be able to clear out the history, then spawn a new window (without a location line) and when the user is done, close the window. The URLs will still be in the browser's history list ( cntrl-h ), but the session's backward and forward button list will have been destroyed. ConclusionReally, manipulation of the browser's history is unnecessary as long as you
are dynamically generating your web pages and you use And there you go! Everything you need to know to ensure secure sessions, stay
secure and private, no matter who sits down at the browser an hour later.
|