A guy I work with asked an interesting question today – how do you deal with users hitting the back button in their browser to take them into an application that they’ve logged out of?
I found an article at JavaWorld that gives an interesting, albeit slight confusing, approach. I made some notes based on that here, and offer some slight improvements.
To state the problem – a user (Alice) logs in to a website, does whatever, and logs out. Another user (Eve) comes along an presses the back button; the web application should not show any of the pages from the Alice’s session (or indeed, the site) until the next correct login is given.
There are three aspects to this – preventing cacheing, forcing login, and preventing Login from a stored page.
Preventing caching is straight forward enough – you send the appropriate headers, expiry dates, etc.. That’s not very interesting – look it up somewhere else…
Likewise, forcing login isn’t all that interesting. You’ll need some sort of session mechanism (most worthwhile language have LOTS of help with this), so for each session, you simply store whether the user is logged in. If not, take them to the login page. Do that for each script that you want restricted. Not very hard. Servlets can have all sorts of security configured in the Web.xml file, so it’s straight forward enough to protect pages.
But, this is where it gets more interesting – what if Eve presses the back button until she gets to Alice’s login page?
It’s worth considering what happens when the back button is pressed. If a page was requested using GET, then it is shown. If a page has Post data, a message is displayed – something like
Warning: Page has Expired
The page you requested was created using information you submitted in a form. This page is no longer available. As a security precaution, Internet Explorer does not automatically resubmit your information for you.
To resubmit your information and view this Webpage, click the Refresh button.
If Eve then hits refresh, she’ll be logged back in – as Alice. This is not desirable.
To prevent this, the login page should contain a hidden field – the System Time of the server when the login page was served (we’ll call this SysTime). In the database, the User record should have a field of when the user last logged in (we’ll call this DBTime). For a login to be allowed SysTime must be later than DBTime. If the login is allowed, the DBTime is updated to be SysTime.
Naturally, you also want to do all you usual user authentication – valid username, correct password, etc..
Great! Now, what if someone tries to spoof the SysTime value? What if they create the HTTP request using the user’s username and password, but another time?
Well, to prevent that, don’t serve the time, but the time encrypted. Use a block cipher – that is a simple one, with one password to encrypt and decrypt. And also store with the time the session ID. By the time you’re trying to authenticate, the session should have been initialised. Consequently, the value of the hidden field will be a time, encrypted, that is specific to that session.
When the form is submitted, unencrypt the hidden field. If the session ID stored in it and the ID of, well, the session don’t match, someone is trying to spoof the session. If they do match, but the SysTime value is older than the DBTime, then this is an old login. Otherwise, it is a true login, and let the user in.
Spoofing the Login Time – Can’t be done! It’s encrypted, and specific to that session.
‘Down Time’ due to client machine being on a different time – Doesn’t matter! It’s the SERVER’s time that matters, the client can be in NeverNeverland time as far as the server is concerned.
‘Down Time’ due to daylight savings time changes – just always use a fixed time reference, such as GMT.