Lo bueno de tener un tester que te odia (ddaz) y un BOFH al que no le eres grato (DrModding), es que tus aplicaciones deben ser seguras, rápidas y confiables. Puesto que serán los primeros en quejarse de tu aplicación. Por eso tengo que estar siempre optimizando todo y volviéndolo mas seguro. Por eso ahora que andaba optimizando a ZOD, me tope con que las sesiones no eran seguras, por lo cual me hice a la tarea de volverlas un poco mas seguras por parte de la aplicación, porque aunque hay un procedimiento para hacerla desde el servidor, no creo que el BOFH me de permisos de root. Así que comencemos:
La sesiones son en si, se envían y se guardan en el cliente, por eso son fácilmente manipulables por un usuario mal intencionado. Por ello lo recomendable es mandar las secciones encriptadas, ademas de que hay que verificar el tiempo de respuesta del usuario (porque el tiempo de vida de la sesión se puede modificar por el usuario). Así que para no complicarme; cree una clase de PHP de sesiones seguras. La explicación me la brincare, porque esta fácil de entender el código:
<?php
class sesion {</p>
private clavex = '';
// _SET: es para agregarle un valor a una sesion, de no estar registrada la registra
public function __set($variable,$valor){
if(session_is_registered(md5($variable+$this->clavex))==false)
$this->registrar(md5($variable+$this->clavex));
$_SESSION[md5($variable+$this->clavex)]=base64_encode($valor);
}
// _GET: Obtiene el valor de una sesion, de no estar registrada regresa false
public function __get($variable){
if(session_is_registered(md5($variable+$this->clavex)))
return base64_decode($_SESSION[md5($variable+$this->clavex)]);
else
return false;
}
// Registra la variable como sesion
private function registrar($variable){
if(!session_is_registered(md5($variable+$this->clavex)))
session_register(md5($variable+$this->clavex));
}
// Borra y destruye una sesion
public function borrar($variable){
$_SESSION[md5($variable+$this->clavex)]='';
session_unregister(md5($variable+$this->clavex));
}
// Destruye por completo todas las sesiones
public function limpiar(){
session_unset();
session_destroy();
}
// Crea un ID de sesion relacionado a una llave y a la IP del usuario
public function __construct($clave=NULL,$tiempo=60){
if(is_null($clave)){
self::limpiar();
exit;
}elseif(!is_null($clave)){
$ip=(getenv("HTTP_X_FORWARDED_FOR"))?getenv("HTTP_X_FORWARDED_FOR"):getenv("REMOTE_ADDR");
if(md5(md5($clave)+md5($ip)))==(session_id()))and(time()-base64_decode($_SESSION[md5('SESSION_TIME')])<$tiempo){
session_id(md5(md5($clave])+md5($ip)));
session_start();
$_SESSION[md5('SESSION_TIME')]= base64_encode(time());
$this->clavex=$clave;
}elseif((!session_id())and($tiempo==0){
session_id(md5(md5($clave)+md5($ip)));
session_start();
$_SESSION[md5('SESSION_TIME')]= base64_encode(time());
$this->clavex=$clave;
}else{
self::limpiar();
exit;
}
}else{
self::limpiar();
exit;
}
}
}
Bien, en si la clase, verifica la sesión por medio de una llave que solo se conoce en el servidor, así como la IP del usuario (por si llegaran a atrapar la sesión no podrá ser usada mas que por la IP con la que se genero). Así mismo, se agrega una sesión de tiempo con el tiempo encriptado en base64 (como mencione antes, esto es solo para el ejemplo, siempre recomiendo crear su propia encriptacion). La forma de usar esta clase, es incluyendo el archivo y crear el objeto de sesión:
<?php
$sesion=new sesion('llave',[tiempo_de_la_sesion]);
$sesion->nombre_de_la_sesion = valor_de_la_sesion;
$valor = $sesion->_nombre_de_la_sesion;
echo $valor;
La forma de usarlo es sencilla. Hay que recordar que la “llave” que incluimos, es tanto para encriptar la sesión como para desencriptarla, por lo cual debe ser la misma; así mismo si no ponemos el tiempo de vida de la sesión, por de facto usara 60 segundos (el tiempo debe ser puesto en segundos).
Actualización: A continuación pongo todo el codigo junto, esta probado y funciona correctamente:
<?php
class sesion {
private $clavex = '';
private $k = '';
// Crea un ID de sesion relacionado a una llave y a la IP del usuario
public function __construct($clave=NULL,$tiempo)
{
if(is_null($clave)){
self::limpiar();
print_r("Autorización no válida. Acceso denegado");
exit();
}elseif(!is_null($clave)){
if(!isset($_SESSION)){session_start();}
$ip=getIP();
$md5=(md5($clave)+md5($ip));
if($tiempo==0){
$this->tiempo=$this->encriptar(time());
$this->md5s=$this->encriptar(md5($clave)+md5($ip));
$this->clavex=$clave;
}elseif((md5($clave)+md5($ip)==$this->desencriptar($this->md5s))and((time()-$this->desencriptar($this->tiempo))<$tiempo)){
$this->tiempo=$this->encriptar(time());
$this->md5s=$this->encriptar(md5($clave)+md5($ip));
$this->clavex=$clave;
}else{
self::limpiar();
print_r('Autorización no válida. Acceso denegado');
exit;
}
}else{
self::limpiar();
print_r('Autorización no válida. Acceso denegado');
exit;
}
}
// _SET: es para agregarle un valor a una sesion, de no estar registrada la registra
public function __set($variable,$valor)
{
if(session_is_registered(md5($variable.$this->clavex))==false)
$this->registrar(md5($variable.$this->clavex));
$_SESSION[md5($variable.$this->clavex)]=$this->encriptar($valor);
}
// _GET: Obtiene el valor de una sesion, de no estar registrada regresa false
public function __get($variable)
{
if(session_is_registered(md5($variable.$this->clavex)))
return $this->desencriptar($_SESSION[md5($variable.$this->clavex)]);
else
return false;
}
// Registra la variable como sesion
private function registrar($variable)
{
if(!session_is_registered(md5($variable.$this->clavex)))
session_register(md5($variable.$this->clavex));
}
// Borra y destruye una sesion
public function borrar($variable)
{
$_SESSION[md5($variable.$this->clavex)]='';
session_unregister(md5($variable.$this->clavex));
}
// Destruye por completo todas las sesiones
public function limpiar()
{
session_unset();
}
// Funcion para saber cuantos
public function usuarioEnlinea() {
$count=0;
$handle=opendir(session_save_path());
if($handle==false) return-1;
while (($file = readdir($handle))!=false)
if (ereg("^sess",$file))
if(time()-fileatime(session_save_path().'/'.$file)<120) // 120 secs = 2 minutes session
$count++;
closedir($handle);
return $count;
}
//Funciones para encriptamiento propietario de las sesiones
private function ed($t)
{
$r = md5($this->k);
$c=0;
$v = "";
for ($i=0;$i<strlen($t);$i++) {
if ($c==strlen($r)) $c=0;
$v.= substr($t,$i,1) ^ substr($r,$c,1);
$c++;
}
return $v;
}
public function encriptar($t)
{
if(!is_array($t)){
$t=urlencode($t);
srand((double)microtime()*1000000);
$r = md5(rand(0,32000));
$c=0;
$v = "";
for ($i=0;$i<strlen($t);$i++){
if ($c==strlen($r)) $c=0;
$v.= substr($r,$c,1) .(substr($t,$i,1) ^ substr($r,$c,1));
$c++;
}
$v = base64_encode($this->ed($v));
$v = str_replace('+','AbCdE',$v);
return $v;
}else{
foreach($t as $x=>$y)
$s[$x] = $this->encriptar($y);
return $s;
}
}
public function desencriptar($t)
{
if(!is_array($t)){
$t = str_replace('AbCdE','+',$t);
$t = $this->ed(base64_decode($t));
$v = "";
for ($i=0;$i<strlen($t);$i++){
$md5 = substr($t,$i,1);
$i++;
$v.= (substr($t,$i,1) ^ $md5);
}
return urldecode($v);
}else{
foreach($t as $x=>$y)
$s[$x] = $this->desencriptar($y);
return $s;
}
}
}
?>
ACTUALIZACION: Faltaba definir una funcion getIP(), cuya función es optener la IP real (o lo mas real posible) del usuario.
<?php// Verifica la ip real de un usuario
function getIP(){
if(getenv("HTTP_CLIENT_IP")&&strcasecmp(getenv("HTTP_CLIENT_IP"),"0.0.0.0")){
$ip=getenv("HTTP_CLIENT_IP");
}elseif(getenv("HTTP_X_FORWARDED_FOR")&&strcasecmp(getenv("HTTP_X_FORWARDED_FOR"),"0.0.0.0")){
$ip=getenv("HTTP_X_FORWARDED_FOR");
}elseif(getenv("REMOTE_ADDR")&&strcasecmp(getenv("REMOTE_ADDR"), "0.0.0.0")){
$ip=getenv("REMOTE_ADDR");
} elseif(isset($_SERVER['REMOTE_ADDR'])&&$_SERVER['REMOTE_ADDR']&&strcasecmp($_SERVER['REMOTE_ADDR'],"0.0.0.0")){
$ip=$_SERVER['REMOTE_ADDR'];
}else{
error('Autorización no válida. Acceso denegado');
exit;
}
return $ip;
}
Tags: clase, PHP, segura, Seguridad, sesion, sesiones
Me pudieras enviar por favor la clase, creo q el inicio está medio cortado, estoy intentando hacer varios sitios más seguros.. cosa q me llevará un buen rato terminar de entender todo esto… saludos!!
Hola,
muy buen ejemplo de como manejar las sesiones con clases y clase
Sólo un comentario para la gente que llega aquí, y es que usar la IP no es muy recomendable en sitios web (para intranets no hay problema), ya que por ejemplo los usuarios de AOL (y algunos otros) cambian de IP más de una vez durante la sesión.
Eso sería, saludos!
Muy buenas,
me parece muy interesante esta clase. Pero para servidores que tengan versiones anteriores de PHP (me refiero a PHP 4) no funciona no.
Saludos!
No funcióna la clase me manda error en “private clavex = ”;” alguien tiene alguna idea de por que?
De antemano gracias
Cuauhtémoc efectivamente traía un error, pero ya lo corregi y puse una actualización con todo el codigo junto para que puedas usarlo.
Hola que tal Antioroku gracias por responder, otra duda, en la línea 14 del código $ip=getIP() la función getIP no está definida en el código, de antemano gracias
Se me paso esa funcion, pero ya la he actualizado Cuatémoc.
podrias poner algun ejemplo de su uso por favor esta muy completo, me gusto.
lo voy a poner en uso.
saludos!