Haciendo seguras las sessiones PHP
By runix el 11/09/2002 10:00
Las sesiones de PHP son una herramienta muy usada en el desarrollo
de web: permiten 'recordar' datos del usuario entre una visita y
otra. Un ejemplo tipico es recordar que un usuario esta loggeado
en un sitio, para que no tenga que ingresar su password nuevamente
cuando vuelve a entrar.
Ivan R. describio en un documento como es posible tomar el control
de sesiones abiertas por otros usuarios de maneras bastante sencillas
(esto trae muchos problemas se seguridad, como podran imaginarse).
Aqui les propongo unas piezas de codigo que hacen mucho mas seguras
las sesiones: al encriptar ciertos datos, se vuelve imposible que
otros usuarios puedan espiar las sesiones de los usuarios y tambien
impide que malvados hax0rs tomen el control de nuestras sesiones.
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
Usuarios que han visto este tema también han visto...
- Crear archivo XML para sindicación RSS con PHP
- Ocultar link de descargas en PHP
- Como pasar un array de un script.php a otro.php
- Redondear decimales en PHP
- Utilizar las Cookies en PHP
Información legal | Política de Privacidad | Contácte con nosotros
Otro proyecto de Factoría de Internet. Copyright© 2003-2008 Factoría de Internet S.L.. All Rights Reserved.