Документация
HTML CSS PHP PERL другое

Session Security

 
Previous
Table of Contents
Next

Session Security

Given that sessions are frequently used to help users navigate through parts of our web application that contain sensitive information (bank accounts, credit card transactions, or medical records), they are one of the features likely to be attacked and exploited. We have to pay particular attention, as we write our code, to help reduce the likelihood of an attack succeeding.

The key to session security is the session ID. Attacks against sessions are largely based on obtaining this, which the attacker can use to access the web application with the same privilege levels as the user from whom the ID was stolen.

We will focus our security efforts on two main areas:

  • Preventing attackers from obtaining users' session IDs

  • Limiting the potential damage to your system if an attacker succeeds in obtaining a valid session ID

Obtaining the Session ID

Attackers devote a good amount of time to trying to obtain a valid session ID of an existing user. With this, they can move around the system as that user with all their capabilities.

Although session IDs are hard to guess or predict (the algorithms that PHP uses to generate them are quite robust), there are a number of ways that an attacker can try to obtain one.

In previous chapters, we mentioned that plain text traffic is easily viewable by some attackers, and having the session ID in the URL or in a cookie traveling over unencrypted connections can be troublesome. Thus, we must consider using HTTPS for session-related traffic whenever possible. We have also seen that passing the session ID in the URL (as a GET parameter) is particularly unsafe since it may end up in the browser history cache, which could be easily read.

However, an attack that is more difficult to visualize is one where the attacker provides a session ID for us. If the user's system or a web site on the Internet is compromised through a cross-site scripting attack (see Chapter 16, "Securing Your Web Applications: Planning and Code Security"), the user might be redirected to a site that inserts the following into the URL before sending him on to your web application:

?PHPSESSID=234893248293478239482349328

When the user then goes to view our web application (which calls session_start), it sees that there is no data associated with that session ID and creates some. Our users do not know what has happened, but the attacker now knows the session ID with which he is working his way through our application.

There are two ways to stop this:

  • Verify that session.use_only_cookies is turned on in php.ini. If this is the case, PHP will refuse to work with URL-based session IDs.

  • Whenever we start a session (even if we are using cookie-based sessions), seed the session data with a variable indicating that we created that session and its storage. After we have done that, we can call session_regenerate_id, which assigns the existing session a new session ID, as follows:

    <?php
      session_start();
    
      if (!isset($_SESSION['created']))
      {
        session_regenerate_id();
        $_SESSION['created'] = TRUE;
      }
    
    ?>
    

Limiting Damage from a Compromised Session ID

Because we can never guarantee that an attacker is unable to get a valid and active session ID, we should try to make it as difficult as possible for him if he gets hold of one. Instinct might tell us to try to associate the user's session with his IP address or domain name.

Unfortunately, this does not tend to work very well since many large ISPs have users that share public IP addresses and host names; proxy servers might even cause a user's IP address to change between individual requests!

However, one piece of information that is commonly sent along with requests is the HTTP User-Agent header. This reflects the exact browser and operating system combination with which the user is currently accessing your web application. When a user logs in, we store this in his session data, as follows:

<?php

  session_start();

  if (!isset($_SESSION['user_agent']))
  {
    $_SESSION = $_SERVER['HTTP_USER_AGENT'];
  }
  else
  {
    if ($_SESSION['user_agent'] != $_SERVER['HTTP_USER_AGENT'])
    {
      // POSSIBLE SECURITY VIOLATION
      // re-prompt for password and re-create session ID
    }
  }

?>

Since the User-Agent is not likely to change between requests, this can help us thwart the attacker. Unfortunately, the attacker might be clever and try a few common values of User-Agent strings until he gets it right. Then your system is compromised again.

To design a better solution, we might do the following:

1.
Make a hash of the User-Agent with some additional string data by using a function, such as md5. (A hash function takes data from an arbitrarily large set and converts it into different-looking data that is often more compact. The resulting hash is completely reproducible and is not likely to have been generated by another input.) We add some extra data to the User-Agent string so that the attacker cannot try to md5 encode common agent values.

2.
Save this encoded string in the session data for the user.

3.
Verify the hash every time we receive a request from the user.

The code for this might look as follows:

<?php

  define('UA_SEED', 'WEBAPP');

  session_start();

  if (!isset($_SESSION['user_agent']))
  {
    $_SESSION['user_agent'] =
        md5($_SERVER['HTTP_USER_AGENT'] . UA_SEED);
  }
  else
  {
    // if the user agent doesn't match or he didn't give us
    // our user-agent-encoded token, revalidate the session.
    if ($_SESSION['user_agent'] != 
        md5($_SERVER['HTTP_USER_AGENT'] . UA_SEED))
    {
      // POSSIBLE SECURITY VIOLATION
      // re-prompt for password and re-create session ID
    }
  }

?>

By throwing a few roadblocks in front of the attacker, we can hopefully reduce or prevent his ability to do damage, even if he gets his hands on a session ID.


Previous
Table of Contents
Next
© 2000- NIV