Monday, November 20, 2006

Multiple Tabs and Windows with session_regenerate_id()

I was asked a week ago to improve the security of the login system used for one of the sites I manage. Prior to being asked the data displayed on the site was not of a sensitive nature - but a recent project I have been working on requires a little more security.

I decided to do two things:
  1. I added a Javascript to MD5 hash strings. Within the login form a random string is placed in a hidden field. This is stored in the server side session. Upon pressing the submit button this is concatenated to the password and the entire string it Md5 hashed.
    • This first ensures the password is not sent in plain text across the unsecured connections.
    • Second, it ensures that an intercepted Md5 string cannot be used again to logon. As it is generated using of the password and the random string fixed to the users session.
  2. Additionally I decided to use session_regenerate_id() with every single request. This would mean the session ID is only ever good for one request. Upon changing the session ID, the old session was of course deleted.
The first change worked fine and for those who don't have Javascript enabled, the unencrypted password and login string is used. However, a couple of days after sending out the update for the site I started getting complaints from some users that they had to keep logging on.

Upon viewing the logs, it was clear that some users were having to logon several times during their session. I did some investigation and came up with the following cause.
  • It was clear from the logs that the cookie being sent contained the old session ID that had not been updated on the users PC after the call to session_regenerate_id().
  • As the old session had been deleted it appeared as if the user had attempted to request the service without even starting a session and logging on.
  • The previous valid request was at exactly the same time or less than 2 seconds before the failed request.
After servicing a request, I have set the application up to dispatch the request to another script (designed only to display the result) via an HTTP redirect using the Location header. I concluded that the redirect was taking place before the web browser had a chance to update the cookie and as such sent the old session ID.

The reason for this? - being unable to replicate the error on my system I concluded that the users PC was not fast enough to update the cookie before redirecting. Therefore I modified the application to ensure that session_regenerate_id() is only used on a request that has not been redirected. I sent the update and hoped this would fix the problem.

Today however, I checked the logs and it was clear that a number of users were still experiencing the same problem despite the modification. I needed to revisit the problem!!

I trawled the logs again and after some time noticed the previous request was within a second of the failed request again. How was this possible? How can one person make two requests in such quick succession? After some thought it came to me - it is just possible that the user has multiple windows/tabs open on their desktop. They are typing in the data (in this case a single order number) and clicking the submit button in each window, one after the other. So, I opened a few windows in Firefox and entered a few order numbers. Then clicked the submit button in each window at a modest speed. Unsurprisingly, this replicated the error and is the most likely cause of the problem the users are experiencing.

I have temporarily disabled the call to session_regenerate_id() while I come up with a solution. It demonstrates once again the varying ways in which your users will use your application and how crucial the logs were (in this scenario) to solving the problem. An example of which is posted below:

Mon, 20 Nov 2006 08:04:38 +0000 123.123.123.123 /ordersearch_display.php
User agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)
Get Variables: Array()
Post Variables: Array()
Cookies: Array( [sid] => 5442a47367571d58101184c1c71df2c4)
User Authenticated: USERNAME
Module: ordersearch loaded.
Module: ordersearch executed for output.
Regenerating Session ID old=5442a47367571d58101184c1c71df2c4 new=5e507a0b72f3dc47501f60316721e48
--------------------------------------------------------
Mon, 20 Nov 2006 08:04:40 +0000 123.123.123.123 /ordersearch_input.php
User agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Get Variables: Array()
Post Variables: Array( [order] => Array ( [ordernr] => 12345 [ord_seq] => ) [submit] => Retrieve Order Data)
Cookies: Array( [sid] => 5442a47367571d58101184c1c71df2c4)
User Not Authenticated

1 comment:

Adam said...

I did solve the problem in the end. I required a substantial change to the session handling for the site.

I rewrote the session handling procedures manually and made the regenerate ID save a backup of the old session which was good for 10 seconds after the regeneration.

This solved the problem and the site is now working fine.