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.