Las sesiones de PHP
funcionan asi: cuando el usuario entra por primera vez al
sitio, se le envia una cookie que contiene un identificador
(llamado session id o SID). Luego desde el PHP se pueden
asociar datos a este identificador. Cada vez que el usuario
entra al sitio, el sistema de sesiones trae los datos que
corresponden a este usuario (en realidad, los que corresponden
al identificador de la cookie). Tambien PHP ofrece otra
manera de pasar el SID, cuando browsers no soportan cookies:
en estos casos, es posible pasar el SID por el
URL.
Los problemas que aparecen son los
siguientes: 1) Si uno logra averiguar cual es el
Session ID de una session, es posible pasar este dato via GET
o creando una cookie y tomar el control de la sesion. Lo
que lo hace peligroso es que conseguir el SID es bastante
sencillo: *) Los datos de las sessiones estan almacenados en
/tmp/ (por defecto, aunque se puede cambiar) y tienen de
nombre: sess_SID(por ej, sess_DAQSAFKOAKDOASK) *)
Cuando el PHP pasa el SESSIONID por el URL y el usuario hace
click sobre un link a una pagina externa, esta recibe como
dato cual fue la pagina anterior en la que el usuario estuvo
(REFERER_URL) que incluye el SID. PHP ofrece una manera de
evitar que se activen sesiones con el SID en el URL si el
usuario viene desde un sitio externo, pero este chequeo es muy
facil de evitar
2) Cuando la aplicacion PHP esta
alojada en un servidor compartido (webhosting barato),
cualquier usuario puede ver los SESSIONIDs ejecutando
simplemente "ls /tmp/sess_*" Otro problema es que cualquier
usuario con permisos para leer archivos del webserver puede
ver el contenido de las sesiones, con solo abrir el archivo de
session que desee
(/tmp/sess_*)
Soluciones: PHP permite
especificar funciones alternativas para manejar las sesiones.
Usando este feature (session_set_handler) desarrolle unas
funciones que teoricamente solucionan este problema: La
idea es, que aparte de la cookie de session se le envia otra
que contiene una cadena de texto generada al azar. Luego el
nombre del archivo donde estan guardados los datos de la
sesion es generado por el hash MD5 resultante de concatenar el
ID de session y el KEY (o sea,
$filename="sess_sec_".md5($SID.$KEY);). Haciendo esto logramos
que sea imposible deducir el ID de la session viendo el nombre
del archivo que contiene los datos y mas importante, hacemos
que sea imposible tomar el control de una session engañando al
servidor pasandole el SID, porque con un KEY equivocado (o
vacio), el server no encontrara el archivo que contiene los
datos de la sesion y creara una nueva.
Para agregar una
capa mas de seguridad y prevenir otros ataques, los datos de
sesion guardados en el archivo estan encriptados usando la
cadena KEY de password, por lo que solamente el poseedor de la
KEY correcta podra tener acceso a los datos (asi evitamos que
otros usuarios del mismo servidor metan las narices en
nuestras sesiones).
Aun faltan definir algunas
funciones del sistema de sesiones, pero yo no las use nunca,
asi que por ahora, no las voy a implementar
Aunque los
problemas de seguridad que planteo no preocuparan a mas de
uno, es bueno saber que las sessiones no son 100% seguras y
que existen alternativas faciles de solucionar el problema
(solo hace falta insertar las funciones y usar las funciones
de sesion comunmente)
El codigo pueden bajarlo desde aqui o verlo funcionando aqui
/*
This piece of code was developed by Martin Sarsale (martin@n3rds.com.ar)
as a response to the problem shown by Ivan R. (ivanr@webkreator.com) on the
article 'PHP Session Security' (http://www.webkreator.com/php/configuration/php-session-security.html)
This is beta code, Im looking for some suggestions to enhance it!
*/
function sess_open($sess_path, $session_name){
global $_SEC_SESSION;
$sess_sec=ini_get('session.name')."_sec";
# Apart from the session cookie we set another one, with the same name plus
# '_sec' at the end
# On that cookie, we set a random 32byte string (I'll refer to this string
# as 'key')
if (!isset($_COOKIE[$sess_sec])){
$md5=md5(uniqid(''));
setcookie($sess_sec,$md5,ini_get('session.cookie_lifetime'),
ini_get('session.cookie_path'),
ini_get('session.cookie_domain'));
$_SEC_SESSION['int']['key']=$_COOKIE[$sess_sec]=$md5;
$_SEC_SESSION['data']=serialize(array());
$empty=1;
}else{
$_SEC_SESSION['int']['key']=$md5=$_COOKIE[$sess_sec];
}
# The name of the file that contains the session info,
# starts with 'sec_sess_' and it's followed by the md5 string of the
# session_id concatenated with the previous key.
# This avoids people of reading the ID of the session from the session files
# (to hijack the session)
$_SEC_SESSION['int']['filename']=$filename_sec="$sess_path/sec_sess_".md5(session_id().$md5);
if (isset($empty)){
return 1;
}
if (!file_exists($filename_sec)){
fclose(fopen($filename_sec,'w'));
}
if (!$_SEC_SESSION['int']['fd']=fopen($filename_sec,'r')){
$_SEC_SESSION['data']=serialize(array());
return 0;
}
# The data on that file is dedrypted using the previous key
$data_enc=fread($_SEC_SESSION['int']['fd'],filesize($filename_sec));
fclose($_SEC_SESSION['int']['fd']);
if ($data_enc!=''){
$cipher=MCRYPT_DES;
$data=@mcrypt_ecb($cipher,$_SEC_SESSION['int']['key'],$data_enc,MCRYPT_DECRYPT);
}else{$data='';}
$_SEC_SESSION['data']=$data;
$_SEC_SESSION['int']['hash']=md5($_SEC_SESSION['data']);
return 1;
}
function sess_close(){
return true;
}
function sess_read($key){
return $GLOBALS['_SEC_SESSION']['data'];
}
function sess_write($id,$data){
global $_SEC_SESSION;
$sd=$data;
if ($_SEC_SESSION['int']['hash'] != md5($sd)){
$fd=fopen($_SEC_SESSION['int']['filename'],'w');
$cipher=MCRYPT_DES;
# Here we crypt the data with our key...
$data=@mcrypt_ecb($cipher,$_SEC_SESSION['int']['key'],$sd,MCRYPT_ENCRYPT);
fputs($fd,$data);
fclose($fd);
chmod($_SEC_SESSION['int']['filename'],0600);
}
}
function sess_destroy($key){
return(@unlink($GLOBALS['_SEC_SESSION']['int']['filename']));
}
function sess_gc($maxlifetime){}
session_set_save_handler('sess_open','sess_close','sess_read','sess_write','sess_destroy','sess_gc');
session_start();
if (!isset($_SESSION['times'])){
$_SESSION['times']=0;
}
$_SESSION['times']++;
print "This session ID is: ".session_id().
" but the name of the file that contains the data is ".
$_SEC_SESSION['int']['filename']." \n";
print "Btw, this is the ".$_SESSION['times']." you see this page ;) (it works!) \n";
?>
Espero que les sirva! :) Martin Sarsale, aka
runa/runix, martin@malditainternet.com
|