Encriptar sesiones utilizando mcrypt en php

Bueno por todos es conocido que las sesiones en php tienes sus vulnerabilidades, vaya que no son 100% efectivas en cuanto a seguridad, pues estas se almacenan en unas cookies en el servidor en cuyo caso si se tiene acceso a esta carpeta se podrían leer. Para evitarlo hay diversas maneras una es almacenando los datos de sesión en un servidor de bases de datos o codificando estas cookies.
La desventaja del primero aunque es más seguro es la saturación del servidor SQL en caso de que existan muchas variables y muchos usuarios, por eso me parece una forma segura y fiable la codificación de estas cookies.
Este código es una modificación del código de Martin Sarsale (martin@n3rds.com.ar) en el cual utiliza funciones ya deprecadas en php. Además le aumento la seguridad utilizando constantes definidas en otros archivos y el vector de inicialización.

define("INIT_VECTOR","36452318");

function sess_open($sess_path, $session_name){
global $_SEC_SESSION;
$sess_sec=ini_get('session.name')."_sec";

# aparte de la cookie de la sesion se crea otra terminada en
# '_sec' donde se almacena una clave aleatoria de 32 byte (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];

# el nombre del archivo que contendrá la infrmación de la sesión empieza,
# con 'sec_sess_' y está seguido por el md5 del session_id concatenado con la clave
# santerior key

$_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;
}

# Los datos son desencriptados utilizando la clave key
if(filesize($filename_sec) > 0)
$data_enc=fread($_SEC_SESSION['int']['fd'],filesize($filename_sec));
fclose($_SEC_SESSION['int']['fd']);
if (isset($data_enc) && !empty($data_enc)){
$cipher = mcrypt_module_open(MCRYPT_BLOWFISH,'','cbc','');
mcrypt_generic_init($cipher, $_SEC_SESSION['int']['key'], INIT_VECTOR);
$data = mdecrypt_generic($cipher,$data_enc);
mcrypt_generic_deinit($cipher);
}
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_module_open(MCRYPT_BLOWFISH,'','cbc','');
mcrypt_generic_init($cipher, $_SEC_SESSION['int']['key'], INIT_VECTOR);
$data = mcrypt_generic($cipher,$sd);
mcrypt_generic_deinit($cipher);
fputs($fd,$data);
fclose($fd);
chmod($_SEC_SESSION['int']['filename'],0600);
}
}

function sess_destroy($key){
return(@unlink($GLOBALS['_SEC_SESSION']['int']['filename']));
}
// Importante dejar esta función vacia
function sess_gc($maxlifetime){}

// la siguiente función es para evitar poner todo esto en cada página que necesitemos crear
un session_start() ahora solo es necesario incluir con include_once este archivo con las funciones e inicializamos la sesión con sess_create.

function sess_create(){
// por defecto en php el save_handler esta en files, debemos cambiarlo a user para poder gestionar nosotros mismos las funciones de sesión
ini_set("session.save_handler", "user");
// con este asociamos las funciones por defecto de php del control de sesiones con las nuestras, ver manual de esta función para detalles
session_set_save_handler('sess_open','sess_close','sess_read','sess_write','sess_destroy','sess_gc');
session_start();
}

Comentarios

golinmarq ha dicho que…
Saludos! Gracias por la informacion en tu blog.Soy nuevo en esto y de por lo que se me han generado algunas dudas. Hay algo que aún no me queda claro y es que debo hacer para acceder a la información de las sesiones. ¿Qué función debería usar en este caso? Por otro lado, ¿cómo podría hacer para crear una cookie de sesion? ya que no me queda claro la razon por la que el parametro $key no se usa en funciones como sess_read
Reynier de la Rosa ha dicho que…
Hola, intenta´re recordarlo, solo decirte que esto es de hace 7 años y realmente nunca lo he dejado en sistemas de producción, el objetivo de esto es guardar los datos de las sesiones de usuarios encriptados en la máquina pero no lo recomiendo en absoluto basado en la experiencia. Los valores que se pasan como parámetros en las funciones sess_ son obligatorios porque lo que estamos haciendo es sobre escribir los métodos originales de PHP por los nuestros, acceder a las variables de sesión lo seguirías haciendo con la variable global $_SESSION. ¿A qué llamas cookie de sesión? Saludos
golinmarq ha dicho que…
Saludos Reynier! Muchas gracias por tu respuesta. Basado en tu experiencia ¿Qué recomendarías para hacer el manejo de una sesion de usuario? ¿Recomiendas borrar esta cookie una vez el usuario salga del sistema?. Mi pregunta radica a que el sistema almacena en una cookie la información de permisos del usuario. Por otro lado, con cookie de sesión me refería a la variable $_SESSION, de la cual me has respondido.
Reynier de la Rosa ha dicho que…
Pues lo ideal es utilizar $_SESSION tal y como viene por defecto, protejes las paginas de acceso restrigido preguntado si esta definida y tiene un valor por ejemplo $_SESSION['usuarioId']. Lo de finalizar es opción tuya, ten en cuenta que PHP por defecto tienen un tiempo de duración de sesiones, el automaticamente expirará una vez cierre el navegador, lo otro es la tipica pantalla de logout y utilizas session_destroy(). No hace falta mucho más. Luego si quieres ampliar el tiempo que esta logado un usuario puedes apoyarte en cookies de usuario $_COOKIES.
golinmarq ha dicho que…
Ah listo. De verdad muchas gracias por la aclaratoria porque me ha ayudado mucho