Friday, February 15, 2013

PHP Session hacking security in php


Session security is a sophisticated topic, and it's no surprise that sessions are a frequent target of attack. Most session attacks involve impersonation, where the attacker attempts to gain access to another user's session by posing as that user.
The most crucial piece of information for an attacker is the session identifier, because this is required for any impersonation attack. There are three common methods used to obtain a valid session identifier:
  • Prediction
  • Capture
  • Fixation
Prediction refers to guessing a valid session identifier. With PHP's native session mechanism, the session identifier is extremely random, and this is unlikely to be the weakest point in your implementation.
Capturing a valid session identifier is the most common type of session attack, and there are numerous approaches. Because session identifiers are typically propagated in cookies or asGET variables, the different approaches focus on attacking these methods of transfer. While there have been a few browser vulnerabilities regarding cookies, these have mostly been Internet Explorer, and cookies are slightly less exposed than GET variables. Thus, for those users who enable cookies, you can provide them with a more secure mechanism by using a cookie to propagate the session identifier.
Fixation is the simplest method of obtaining a valid session identifier. While it's not very difficult to defend against, if your session mechanism consists of nothing more thansession_start(), you are vulnerable.
In order to demonstrate session fixation, I will use the following script, session.php:
<?php

session_start();

if (!isset($_SESSION['visits']))
{
    $_SESSION['visits'] = 1;
}
else
{
    $_SESSION['visits']++;
}

echo $_SESSION['visits'];

?>
Upon first visiting the page, you should see 1 output to the screen. On each subsequent visit, this should increment to reflect how many times you have visited the page.
To demonstrate session fixation, first make sure that you do not have an existing session identifier (perhaps delete your cookies), then visit this page with ?PHPSESSID=1234 appended to the URL. Next, with a completely different browser (or even a completely different computer), visit the same URL again with ?PHPSESSID=1234 appended. You will notice that you do not see 1output on your first visit, but rather it continues the session you previously initiated.
Why can this be problematic? Most session fixation attacks simply use a link or a protocol-level redirect to send a user to a remote site with a session identifier appended to the URL. The user likely won't notice, since the site will behave exactly the same. Because the attacker chose the session identifier, it is already known, and this can be used to launch impersonation attacks such as session hijacking.
A simplistic attack such as this is quite easy to prevent. If there isn't an active session associated with a session identifier that the user is presenting, then regenerate it just to be sure:
<?php

session_start();

if (!isset($_SESSION['initiated']))
{
    session_regenerate_id();
    $_SESSION['initiated'] = true;
}

?>
The problem with such a simplistic defense is that an attacker can simply initialize a session for a particular session identifier, and then use that identifier to launch the attack.
To protect against this type of attack, first consider that session hijacking is only really useful after the user has logged in or otherwise obtained a heightened level of privilege. So, if we modify the approach to regenerate the session identifier whenever there is any change in privilege level (for example, after verifying a username and password), we will have practically eliminated the risk of a successful session fixation attack.

Session Hijacking

Arguably the most common session attack, session hijacking refers to all attacks that attempt to gain access to another user's session.
As with session fixation, if your session mechanism only consists of session_start(), you are vulnerable, although the exploit isn't as simple.
Rather than focusing on how to keep the session identifier from being captured, I am going to focus on how to make such a capture less problematic. The goal is to complicate impersonation, since every complication increases security. To do this, we will examine the steps necessary to successfully hijack a session. In each scenario, we will assume that the session identifier has been compromised.
With the most simplistic session mechanism, a valid session identifier is all that is needed to successfully hijack a session. In order to improve this, we need to see if there is anything extra in an HTTP request that we can use for extra identification.
Note
It is unwise to rely on anything at the TCP/IP level, such as IP address, because these are lower level protocols that are not intended to accommodate activities taking place at the HTTP level. A single user can potentially have a different IP address for each request, and multiple users can potentially have the same IP address.
Recall a typical HTTP request:
GET / HTTP/1.1
Host: example.org
User-Agent: Mozilla/5.0 Gecko
Accept: text/xml, image/png, image/jpeg, image/gif, */*
Cookie: PHPSESSID=1234
Only the Host header is required by HTTP/1.1, so it seems unwise to rely on anything else. However, consistency is really all we need, because we're only interested in complicating impersonation without adversely affecting legitimate users.
Imagine that the previous request is followed by a request with a different User-Agent:
GET / HTTP/1.1
Host: example.org
User-Agent: Mozilla Compatible (MSIE)
Accept: text/xml, image/png, image/jpeg, image/gif, */*
Cookie: PHPSESSID=1234
Although the same cookie is presented, should it be assumed that this is the same user? It seems highly unlikely that a browser would change the User-Agent header between requests, right? Let's modify the session mechanism to perform an extra check:
<?php

session_start();

if (isset($_SESSION['HTTP_USER_AGENT']))
{
    if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT']))
    {
        /* Prompt for password */
        exit;
    }
}
else
{
    $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
}

?>
Now an attacker must not only present a valid session identifier, but also the correct User-Agent header that is associated with the session. This complicates things slightly, and it is therefore a bit more secure.
Can we improve this? Consider that the most common method used to obtain cookie values is by exploiting a vulnerable browser such as Internet Explorer. These exploits involve the victim visiting the attacker's site, so the attacker will be able to obtain the correct User-Agent header. Something additional is necessary to protect against this situation.
Imagine if we required the user to pass the MD5 of the User-Agent in each request. An attacker could no longer just recreate the headers that the victim's requests contain, but it would also be necessary to pass this extra bit of information. While guessing the construction of this particular token isn't too difficult, we can complicate such guesswork by simply adding an extra bit of randomness to the way we construct the token:
<?php

$string = $_SERVER['HTTP_USER_AGENT'];
$string .= 'SHIFLETT';

/* Add any other data that is consistent */

$fingerprint = md5($string);


Keeping in mind that we're passing the session identifier in a cookie, and this already requires that an attack be used to compromise this cookie (and likely all HTTP headers as well), we should pass this fingerprint as a URL variable. This must be in all URLs as if it were the session identifier, because both should be required in order for a session to be automatically continued (in addition to all checks passing).
In order to make sure that legitimate users aren't treated like criminals, simply prompt for a password if a check fails. If there is an error in your mechanism that incorrectly suspects a user of an impersonation attack, prompting for a password before continuing is the least offensive way to handle the situation. In fact, your users may appreciate the extra bit of protection perceived from such a query.
There are many different methods you can use to complicate impersonation and protect your applications from session hijacking. Hopefully you will at least do something in addition tosession_start() as well as be able to come up with a few ideas of your own. Just remember to make things difficult for the bad guys and easy for the good guys.
Note
Some experts claim that the User-Agent header is not consistent enough to be used in the way described. The argument is that an HTTP proxy in a cluster can modify theUser-Agent header inconsistently with other proxies in the same cluster. While I have never observed this myself (and feel comfortable relying on the consistency of User-Agent), it is something you may want to consider.
The Accept header has been known to change from request to request in Internet Explorer (depending on whether the user refreshes the browser), so this should not be relied upon for consistency.
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?
 $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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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:
$token = md5(uniqid(rand(), true));
Keeping all these in mind should make your applications far more safer so it is definitely worth your while.

No comments:

Post a Comment