One of the most vast misconceptions in the PHP world is how sessions really do work. I'm fairly confident most of us know how to start sessions, terminate sessions, regenerate session IDs and easily pass data from 1 page to another. But do you really know how they work inside out?
A common question I get asked is:
Should you really be storing the users' flags in a session? That's dangerous!
Why is it dangerous? Where else would you store it? Storing crucial information in a session is what sessions are all about. They strive on being able to provide the information without leaving it open to tampering or interception. Giving a session some important information is like giving a Jack Russell a bone - it's not letting go of it anytime soon and if any other sod tries to take the bone, well, I'll leave that up to your gruesome imagination.
Where are Sessions stored?
This is the question that will quash all those questions on are sessions safe. Anything can be unsafe in PHP if the programming is not up to par. Generally speaking, however, sessions are 1 section of the PHP language that is difficult to mess up.
Core of a Session File
Remember the times when you used to crack open the cereal boxes to retrieve the free plastic toy from inside? Well, sessions are not quite as exciting as that. In fact, sessions are pretty uneventful on the inside. Let's take the following PHP code and execute it normally:
As simple as it may be, it does the job. It creates us a session file which contains our session information. The file is stored as the following filename which is made up of 2 parts: it has prefixed us the word sess_ which is then followed by our unique session ID.
If I open up this file it contains the following information which is simply my array which has been serialized (see serialize for further information) and stored.
This is nothing more than my array broken down into a string. When I execute my file again the session ID will be used to retrieve the above data from the relevant session file and unserialized (see unserialize for further information).
That's all there really is to a session file. Remember that the session file is stored server-side so there is absolutely no need to transfer any information you do not publish in the HTML over the insecure medium we like to call the Internet.
Point of the Session ID
Now, this is where our delivery method comes in. Cookies are the most frequently used due to the extra security they add (not much, but just enough to be the favoured method). The other possible methods are GET (which is sometimes used) and POST (which I've never seen used but essentially it could be used with a little difficulty). Unlike GET and POST which can be used in such attacks as CSRF (see our article on CSRF for further information) and passed around to potential victims with far too much ease, cookies are just that little more difficult to install on a victim's computer.
I could quite easily send a link to you and have you either hijack a session on my behalf, or the most common reason would be to fixate your session ID (see the following post for more information about session fixation). Although, if I am unable to pass you the session ID easily then it's going to be more difficult for me to breach the target website's security and assume your identity. Consider the following link and how easily it can be passed around:
You would more than likely click on that link believing it to be perfectly innocuous. The trouble is that I have just set your session ID to 4f4ae - and without any regenerating of the ID you would carry that ID around for the lifetime of your session giving me ample amount of time to assume your role and have almost limitless access to your account.
So where has this all been leading? Well, there is an important setting that is set to 1 by default (and should always be set to 1 unless you wish to also support users who do not have cookies enabled - which is not recommended) that prevents the session IDs from being transferred via a GET and to use only cookies - the safest delivery method. That setting is:
From my earlier example let's take a look at the cookie that has been created on my computer:
The cookie's name is PHPSESSID which is the default name given to a session cookie that is stored client-side. The number you see above is my session ID which will stay with me for the lifetime of my session or until it is regenerated for security purposes.
Upon loading the website where my session ID was created, the cookie is sent to the website and parsed by PHP. The session ID is correctly linked to the session file stored server-side and is then unserialized and placed into the pre-populated PHP array, $_SESSION. Once I have issued the session_start() command to begin my session I am able to access all the information - even crucial and highly sensitive information, from the $_SESSION array.
Note: Although you may store highly insensitive information in sessions, be aware that displaying it on your website will mean that crucial information is being sent over the pipes of the Internet. Without using SSL this would be a very insecure method of delivery which would be crying out to be intercepted by Harry the hacker.
Basic Session Security Example
Assume that our banking system has absolutely no extra security implemented. This is exceedingly unlikely so don't be frightened!. You have logged in and been issued a cookie containing your session ID, our banking system also erroneously accepts the GET method of delivery though to ensure everybody can use their system. Remember, all Harry the hacker (not Harry the hamster) needs to assume your role is your session ID. Nothing more, in this example.
Harry decides to go down the route of session fixation and thus sends you the following crafty link:
You click on it and login. Harry, already knowing your session ID is 5aff2, clicks on the link himself and is able to withdraw your entire life savings of 67 pence.
As sessions do absolutely no extra security checks on your behalf, the system simply thinks it is you because the session ID is correct. It is up to YOU to implement the extra security to prevent such instances from occurring.
Locks up your Daughters
Some mean looking guy approaches your door after trampling on all your pretty flowers without a care in the world, loudly knocks on the door and demands to see your daughter. He may well know your daughter's name but who is he? The who is he part is crucial - it's as crucial as not assuming that because someone has the correct session ID that you should let them in. Until the mean looking guy can answer some of your questions, you're not letting him see your daughter. Oh no, no!
To give a simple question of what you can ask the end user when it comes to sessions is:
Are you using the same browser as you were previously?
This can stop a fair amount of session security breaches but should not be the only method as mentioned later on. If I login to my online banking account using Mozilla Firefox 18.104.22.168, and Harry, after clicking on the exact same link I did, is using Internet Explorer 7.0 or even Mozilla Firefox 22.214.171.124, then you can safely assume 1 of either 2 things have occurred:
You have switched your browser half way through and would like to continue your session seamlessly.
Somebody is attempting to hijack your session using your session ID that they have acquired from somewhere
Which point, 1 or 2, would you rather assume was true? Surely assuming the former and allowing a user to carry on their session seamlessly would be less likely than the latter? Assuming the latter will also save any embarrassing security hiccups. By destroying the user's session and asking them to re-login has to be the better option just in case. Any legitimate user will not mind this slight inconvenience if they know their 67 pence is safe. After all, once the security has been breached, it is difficult to return that level of trust back to its normal levels.
Nonetheless, the browser name, version, language, etc. are all sent by the end user's browser during the HTTP call. This means that this data may not even be sent at all. It is optional. As long as you know it is optional you can check for if the intended array even exists before using it. This is important because a blank string will always produce the same MD5 or SHA1 (etcetera...) hash. A null string MD5'd will always be d41d8cd98f00b204e9800998ecf8427e no matter how many times you use MD5. Whilst SHA1 will be different to the MD5 string, it will still be the same for every single time you hash the null string using SHA1.
Shared Hosts can be Little Buggers
Now that you fully understand the following:
[Session File] (Server) -> [Cookie File] (Client)
You may think your session file, because it is server side, is safe. That simply isn't the case, I'm afraid. A session file can simply be opened by anybody who has access to your file system. For instance, my sessions are stored in the following directory as plain files:
Anybody who has access to that directory can read my session files and read the session IDs from the filenames. This is why shared hosts can be bad. Typically everybody on a shared hosts shares the same temp folder. This is the most common place where session files are stored. As you can see it is a potential downfall to what I've been teaching you in this article (fear not, you haven't just wasted 10 minutes reading a pointless article). Sessions, although server side, are not safe from prying eyes.
Luckily the directory can be moved elsewhere or you can even use a table inside a database to store all your session data. The latter is best saved for another article to keep this 1 short and sweet (ok, shortish and sweetish). The following setting can be changed to alter the destination of our session files:
Remember though that when your website becomes very large and important, storing crucial information. Nothing beats a dedicated host. That means ONLY the people YOU want on your server to have access to your session files can see them, read them and even abuse them.
The next time somebody asks you if sessions are really the way to go, that if storing crucial information in session data is insecure, chuckle in their face and refer them to TalkPHP!
The man who comes back through the Door in the Wall will never be quite the same as the man who went out.
Last edited by Wildhoney : 12-03-2007 at 07:44 PM.
The Following 2 Users Say Thank You to Wildhoney For This Useful Post:
Hi Wildhoney, I must praise you for writing such a brilliant tutorial, this helped me understand sessions alot more. This is the only detailed, understandable sessions tutorial I have read so far. There is only one thing which confuses me, and its about hijacking sessions.
This is how I setup a website im making:
Upon logging in, a session starts and defines $_SESSION['users_id'] = $users_id
($users_id being set after all the SQL queries etc)
This is then passed around the pages to confirm the current user.
Now, upon checking my local cookies - I do not see any text stating "users_id" therefore it would be hard to find the id in the text.
Due to being hard to find could it help with security?
I have the feeling it really doesn't help with security - and now have thought up the more standard sessions method which is:
The user now logs in, and the session id is stored in the table, then to check which user is logged in, I check the $_SESSION['session_id'] against the database and get the users_id to gather the correct data from the users table.
Is it just as easy for them to hijack the session id as it is to hijack the users id? I know this would avoid them hijacking someone elses session WITHOUT getting the session id from them, but if someone passed their session ID onto them - then it defeats the point of adding more functionality.
To be honest, the whole security criteria of PHP worries me as I don't have mass amounts of knowledge to do with sessions and cookies.
Thank you, Adam! I'm glad you enjoyed the article. I'm tired of seeing all these clichés people throw out concerning PHP. We've heard it all before. It just cries out for some originality.
As for your question, I'd definitely go down the storing the session ID route. That way if anybody is wishing to hijack a session, they'd have to either fixate the ID (which we can protect against), or be lucky enough to get a ~32-bit hexadecimal string.
The dilemma of users passing a link, unintentionally or otherwise, to another user, can be prevented by only allowing the session to come from a cookie. This way, the only real way that a session can be hijacked in that sense, is by transferring a cookie to a user and telling them to put it in their cookie folder - and who's silly enough to do that? Sound too much like hard-work to me!
Of course, if you go down the route of preventing the session ID coming from a URL parameter (session.use_only_cookies), then you're not supporting those individuals who have cookies disabled. These days, however, the minority of individuals who have browsers that either do not support cookies, or have their cookies disabled by their users, is negligible when you consider the potential damage that can be caused from session hijacking.
The man who comes back through the Door in the Wall will never be quite the same as the man who went out.
Brilliant, thanks for the speedy response, I will definately be using my stronger session ID from now on, thanks for the update!
Alos Bluesaga, that info has helped me alot, I will now check the session id against the original IP, this will definately avoid any well known ways of session hijacking. Brilliant - I can now make my decision, now to implement it.