Als je gebruik maakt van 'sessies' wordt op de pc van de bezoeker een cookie opgeslagen met hierin zijn SESSION_ID. Elke keer als de bezoeker een nieuwe pagina aanvraagt op jou domein, dan wordt deze cookie meegestuurd zodat jij de bezoeker opnieuw kunt identificeren.
Het gevaar ligt er in dat iemand de SESSION_ID mogelijk kan kopieren, en in aan zijn eigen browser kan toevoegen als cookie. Wanneer deze persoon nu naar jou website gaat, denkt PHP dat het dezelfde bezoeker betreft, omdat deze alleen de SESSION_ID als referentie heeft.
Deze 'feature' noemen we 'session hijacking'.
Wat te doen:
Wat ik standaard doe is o.a. het IP adres & user agent van de gebruiker koppelen aan de betreffende sessie. Wanneer óf het IP, óf de user agent veranderd, reset ik de sessie. Nadeel van deze aanpak is dat iemand met een wisselend IP adres mogelijk opnieuw moet inloggen.
Hier wat PHP code die je inspiratie kan bieden:
<?php
// Define urls & paths
define('ROOT_URL', 'http://www.domainname.tld/';
define('ROOT_PATH', dirname(__FILE__) . '/');
define('SESSION_PATH', ROOT_PATH . 'sessions/');
// Update session path
session_save_path(SESSION_PATH);
// Validate & start session
function __session_start()
{
$sSessionId = __session_start_id();
session_id($sSessionId);
session_start();
// Validate session
if(isset($_SESSION['session']))
{
if($_SESSION['session']['REMOTE_ADDR'] && (strcmp($_SESSION['session']['REMOTE_ADDR'], $_SERVER['REMOTE_ADDR']) !== 0)) // IP has changed during session
{
session_write_close();
$sSessionId = __session_start_id(false); // Force a new ID
session_id($sSessionId);
session_start();
$_SESSION['session'] = array();
$_SESSION['session']['ROOT_URL'] = ROOT_URL;
$_SESSION['session']['REMOTE_ADDR'] = false; // No IP/SESSION lock
$_SESSION['session']['HTTP_USER_AGENT'] = (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');
$_SESSION['session']['HTTP_X_FORWARDED_FOR'] = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : '');
die('<p>Your IP has changed during your session! As a protection against <a href="http://en.wikipedia.org/wiki/Session_hijacking" target="_blank">session hijacking</a> your session data is erased!</p><p>Your session is no longer chained to your IP, so it can support multiple IP addresses from now on.</p><p><a href="' . escapeHtml(ROOT_URL) . '">Please continue</a></p>');
}
elseif((isset($_GET['core']['session']) && (strcasecmp($_GET['core']['session'], 'reset') === 0)) || (strcmp($_SESSION['session']['ROOT_URL'], ROOT_URL) !== 0) || (strcmp($_SESSION['session']['HTTP_USER_AGENT'], (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '')) !== 0) || (strcmp($_SESSION['session']['HTTP_X_FORWARDED_FOR'], (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : '')) !== 0))
{
session_write_close();
$sSessionId = __session_start_id(false); // Force a new ID
session_id($sSessionId);
session_start();
$_SESSION['session'] = array();
$_SESSION['session']['ROOT_URL'] = ROOT_URL;
$_SESSION['session']['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['session']['HTTP_USER_AGENT'] = (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');
$_SESSION['session']['HTTP_X_FORWARDED_FOR'] = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : '');
}
}
else
{
$_SESSION['session'] = array();
$_SESSION['session']['ROOT_URL'] = ROOT_URL;
$_SESSION['session']['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['session']['HTTP_USER_AGENT'] = (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');
$_SESSION['session']['HTTP_X_FORWARDED_FOR'] = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : '');
}
}
// Lookup session id (or generate a new one)
function __session_start_id($bRestoreSession = true)
{
$sSessionName = session_name();
do
{
$sSessionId = randomCode(64, false, true); // Generate unique ID, hexadecimal
}
while(file_exists(session_save_path() . '/sess_' . $sSessionId));
if($bRestoreSession) // Try to restore session
{
if(isset($_GET[$sSessionName]))
{
if(preg_match('/^[0-9a-fA-F]{64,64}$/', $_GET[$sSessionName]))
{
$sSessionId = $_GET[$sSessionName];
}
else
{
unset($_GET[$sSessionName]);
}
}
if(isset($_COOKIE[$sSessionName]))
{
if(preg_match('/^[0-9a-fA-F]{64,64}$/', $_COOKIE[$sSessionName]))
{
$sSessionId = $_COOKIE[$sSessionName];
}
else
{
unset($_COOKIE[$sSessionName]);
}
}
}
else
{
unset($_GET[$sSessionName]);
unset($_COOKIE[$sSessionName]);
}
return $sSessionId;
}
// Create a random code with N digits.
function randomCode($iLength = 64, $bUserFriendly = false, $bHex = false)
{
if($bUserFriendly)
{
/*
Some fonts use (almost) the same symbols for 'i', 'l' and 'one', same goes for 'o' and 'zero'.
That's why we removed these characters from the characterlist (UPPERCASE + lowercase).
*/
if($bHex)
{
$aCharacters = array('a', 'b', 'c', 'd', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F', '2', '3', '4', '5', '6', '7', '8', '9');
}
else
{
$aCharacters = array('a', 'b', 'c', 'd', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '8', '9');
}
}
else
{
if($bHex)
{
$aCharacters = array('a', 'b', 'c', 'd', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
}
else
{
$aCharacters = array('a', 'b', 'c', 'd', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
}
}
$sResult = '';
for($i = 0; $i < $iLength; $i++) // (62 ^ [$digits] mogelijke codes)
{
$sResult .= $aCharacters[rand(0, sizeof($aCharacters) - 1)];
}
return $sResult;
}
?>