The usage of sessions is the php developer’s most common use since we constantly need to transact data from step to step. An average programmer would say that using sessions is far more secure than letÃs say cookies since the session data is server side data, thing that is partially correct.
The fact that the attacker can’t have a clear look at what and where you store comes to your advantage but a more dedicated attacker can go a bit further than this presumption.
A must have for the attacker in a session hijack is the Session Identifier so he can impersonate the attack. Let’s presume for example that you have your website hosted on a shared hosting on which PHP is installed as an Apache module, thing that makes session files belong to the web user, in other words: accessible.
Some more elaborated attacks could be categorized as follows:
Prediction refers to guessing a session identifier, approach that can be rather irrelevant since the native php session identifier is far too random to be analyzed at a glance so the next one can be predicted, meaning the attacker’s focus is not usually here.
Capture on the other hand is the session hijacker’s best practice since it is rather versatile. Having the fact that the session id is being propagated by numerous resorts such as HTTP Headers, Cookies, E-mail Headers and so on, accessing it couldn’t be too hard for the attacker could it?
The worst of this is that each of these ways of session id propagation can be a door for attackers so which one is the best for us? Well, on a small scale, cookies are a bit less exposed than the $GET data for example, so if you have cookies enabled you can work with that as browsers are generally speaking secure – there were just a few miss-behaviors in Internet Explorer.
Fixation is not a very complex method but if you rely your session security on a mere sessionstart() you are in trouble. Since you only work with the default Session Identifier, reproducing a common user’s HTTP Header isn’t that difficult, so how do we work with this?
Let’s base an example on a simple HTTP Header such as:
5 6 7 8 9 | GET / HTTP/1.1 Host: example.org User-Agent: Mozilla/5.0 Accept: text/xml, image/png, image/jpeg, image/gif, */* Cookie: PHPSESSID=1234 |
So we have the HTTP Header in its simplest form yet we have some data to work with. Taking for example that seconds after you get this another request from a different user agent.
As an educated developer one must assume the worst case scenario: this might come from an attacker and not from a user using two browsers. A common use in this case might be re-asking for the user’s credentials, thing that would not impose any issue on the user’s behalf but would disarm the attacker at this stage.
An easy approach to this would be generating a MD5 for the User Agent but than again, MD5 strings are quite easy to recognize so why not using a more elaborated approach and binding a passkey to the algorithm?
8 9 10 11 | $token = $_SERVER['HTTP_USER_AGENT']; $token .= 'ABRACADABRA'; $securetoken = md5($token); |
This approach can help you prevent some of the attacks but not all of them as you can imagine.
Another best practice to preventing the evil session hijack is using the session’s sessionregenerateid() once the user logs in so if his session identifier has been ‘sniffed’ somewhere along the way the chase ends there and so the attacker would have to go all over again.
So we know now how this works, some best practices but how can we make it full proof? Unfortunately, I do not have an answer for the 100% but I have some answers that will make your application safe to most attacks.
Secure practices
- Do not trust users.
Common users are not security educated users so they can easily fall for CSRF attacks and as long as the user is a victim, so are you. If you have any doubt whatsoever on a user’s action do not hesitate to ask for credentials. He wouldn’t mind as long as you tell him that it is for his own protection.
- If on a shared hosting make use of sessionsavepath()
This will allow you to set a new path for your session data storage, a more secure one that the default setting up. Make sure that the folder you store the data is secured and make use of your .htaccess file to limit access there.
- Wondered why there are both $COOKIE and $SESSION?
What you must take into consideration here is that cookies have a different purpose than storing session data so why not leave that as it is? XSS Faltering is quite common so your cookies are not safe.
- Do not pass your session identifier in URLs
By now you should be aware that the session identifier is the attacker’s must have and that your main task is to keep it well hidden so why just serving it to the attacker? As you can imagine this goes for $_GET as well.
- If skeptical on the entire HTTP Headers issue, use a security token at all time
If nothing in the world could determine you to filter every incoming message than the least you can do is to use a security token at all time. A light example of such a token could be:
1
$token = md5(uniqid(rand(), true));
Keeping all these in mind should make your applications far more safer so it is definitely worth your while.
Very interesting and helpful. Thanks.
An important note about sessionregenerateid(): You must make sure to delete the old session. This can be done by passing true into the function or by using session_destroy(). By default, the session is merely copied and not actually renamed, so a compromised sessionID could still be used by an attacker to access a user's account.
Nice observation. Congrats Ian
You can also create a pretty secure token by hashing together not just a piece of random information but by also including the visitor's user agent string as well as IP address.
If they're session is hijacked in some way, the attacker would then have to recreate both of those data or your authorization system could destroy the session and force the recreation of it.
Great input David, but the data you mention is quite easy to reproduce. I am having in mind at this point that most Session Hijacks begin off-site, the attacker using javascript for example to get the user's data that he will later on use on your website.
Having this, the user's ip address and user agent are quite easy to reproduce the given data, so I would rather go to as random as possible.
@Stelian, Go random, too! I generate a random $token (similar to the article above) and store that in my database. Then, hashing that token, the IP address, and the agent string gives me my fingerprint which is stored in the visitor's session.
Every page retrieves the token from the database and rehashes it with the IP and user agent string. Thus, a person has to (a) beat the random token and (b) appear to be the original user. Plus, the $token is never transmitted to the client in anyway unless it's been hashed.
All of that put together has worked for me for a number of years.
@David, now I see what you meant and yes, it is a good practice and stable so the readers here can work with it as well in order to stay secure. Thank you for the input.
Nice and helpful post. Thank you
Using the IP in a "fingerprint", as suggested above by David, is wrong. Users can be behind proxies, and many proxies use different IPs from a pool of assigned IPs on every HTTP request. Consecutive HTTP requests from the same user behind a proxy are not guaranteed to be coming from the same IP.
Hi, Can anyone show how to use tokens ? I am new to sessions. Thanks.
u can use salt token generations to make u session really hard to steal
Brilliant article. One of the hardest lessons I learnt... And looked real dumb when it was pointed out is... that you should not pass session identifier in URLs!
You have much to learn young Anakin Skywalker :)
There are other security related articles which you can read here: You should check it out!
4 Most Important PHP Security Measures
Securiy Measures against CSRF Attacks
Nice blog!! But want to ask one thing when cookies are disabled then we pass session identifier in URL then how can security be guaranteed .
@Siddesh It is strongly advised against passing session ids passed in the URL.
The main problem is that the session id will be visible in the referrer HTTP header and the hacker will have easier time to hijack it.
Instead passing in the URL, why not use a hidden form field and pass it there.
Or if you still don't have other options then at least encrypt the session id then pass it in the URL.