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