Extensive PHP Articles, Resources and Links

Wednesday, May 25, 2005

PHP SESSION SECURITY :

Outline

The structure of this talk is free-flowing, so feel free to ask questions at any time.

Session Overview

Session Fixation

Session Hijacking

More Information

Open Discussion

In the Beginning...

The Web was without state.

What does this mean?

It refers to the isolated way in which each HTTP transaction is treated - there is no relationship among the requests originating from the same client.

HTTP?

HTTP is the protocol that powers the Web.

Example Request:

GET / HTTP/1.1
Host: example.org
User-Agent: Mozilla/5.0 Gecko
Accept: text/xml, image/png, image/gif, */*

Example Response:

HTTP/1.1 200 OK

Server: Apache/1.3.31 (Unix)
Content-Type: text/html
Content-Length: 57
Connection: close
 

Here is the resource you requested.

     

Cookies

Netscape created an extension of the HTTP protocol that allows for stateful HTTP transactions.

These "cookies" consist of two new headers:

Set-Cookie response header

Cookie request header

Cookies in Action

It takes two complete transactions to determine whether cookies are enabled:

Sessions

PHP makes sessions EASY:



session_start
();
/* Use $_SESSION for session data */
?>

session_start()

What does session_start() do?

  • Checks for session identifier (PHPSESSID)
  • Creates record in session store for that identifier
  • Sets caching headers and Set-Cookie

Session Fixation

Three ways to get a valid session identifier.

  • Prediction
  • Capture
  • Fixation

session_start() isn't enough.



session_start
();

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

echo
$_SESSION['visits'];

?>


session_regenerate_id is your friend.




session_start
();

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

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

echo
$_SESSION['visits'];

?>

Regenerate the session identifier when there is a change in privilege level.




session_start
();

if (
auth($_POST['username'],
$_POST['password']))
{
$_SESSION['logged_in'] = true;
session_regenerate_id();
}
else
{
$_SESSION['logged_in'] = false;
}

?>

Session Hijacking

Again, session_start() isn't enough.

Don't use IP address for identification!

Assume the session identifier is captured.

Complicate impersonation.

Find consistency within requests.


GET / HTTP/1.1
Host: example.org
User-Agent: Mozilla/5.0 Gecko
Accept: text/xml, image/png, image/gif, */*
Cookie: PHPSESSID=1234

Is User-Agent consistent?




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']);
}

?>

Use a fingerprint for more protection.




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

/* Add any other data that is consistent */

$fingerprint = md5($string);

?>

If you don't trust the consistency of anything, a unique token is better than nothing.




$token
= md5(uniqid(rand(), true));

$_SESSION['token'] = $token;

?>

Propagation

Propagate the browser fingerprint (or session token) by a different method than the session identifier when possible.

Propagating the session identifier in a cookie and the browser fingerprint (or session token) on the URL is a good approach.

PHP Security Articles :

Link 1