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.