ene
30
2008

PHP – Clase de Sesiones seguras

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;
}
Written by Antioroku in: Informatica | Etiquetas: , , , , ,

19 comentarios »

  • Chava dice:

    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!!  (Seleccionar comentario)

  • Patricio Estrella dice:

    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!  (Seleccionar comentario)

  • jorcanvi dice:

    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!  (Seleccionar comentario)

  • Cuauhtémoc Torres Vela dice:

    No funcióna la clase me manda error en “private clavex = ”;” alguien tiene alguna idea de por que?
    De antemano gracias  (Seleccionar comentario)

  • Antioroku dice:

    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.  (Seleccionar comentario)

  • Cuauhtémoc dice:

    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 :D   (Seleccionar comentario)

  • Antioroku dice:

    Se me paso esa funcion, pero ya la he actualizado Cuatémoc.  (Seleccionar comentario)

  • sikel dice:

    podrias poner algun ejemplo de su uso por favor esta muy completo, me gusto.
    lo voy a poner en uso.
    saludos!  (Seleccionar comentario)

  • Jorge dice:

    Oie una consulta, mira yo se programar en java y reconosco que la forma de trabajar con clases es bien similar, llevo ya igual un buen tiempo metiendome en php y me cuesta entender algunas cosas, por ejemplo

    # 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);
    # }

    La forma en que tratas al objeto agregandole el valor de this si me podrian explicar la forma en que trabaja this en esta clase  (Seleccionar comentario)

    • Antioroku dice:

      Mira la cuestión esta así:
      <?php
      class sesion {
      private $clavex = ”;
      private $k = ”;

      Al principio de la función, declaro una variable privada llamada $clavex. Para poder usar esta variable privada dentro de las función (o cualquier otra) debes utilizar la sintaxis $this-> (refiriéndose a la clase en cuestión, en este caso class sesion) y luego la funcion o variable que deseas utilizar o accesar. En este caso en particular se refiere a la variable privada $clavex, por eso se usa $this->clavex (clavex ya no lleva el simbolo $ cuando es llamada de esta forma).  (Seleccionar comentario)

  • Marco Antonio dice:

    Antioroku esta excelente la clase que compartes, me la he pasado estudiando, estoy haciendo un sitio modular en php, y quiero manejar sesiones para que puedan acceder a unas paginas que necesiten privilegios, la autentificacion del usuario lo manejo normal con un text de usuario y otro de password, y ahi es donde quiero implemenetar tu aporte, podrias poner un ejemplo de como seria, si no es mucha molestia gracias, esta excelente.  (Seleccionar comentario)

  • jack dice:

    algun ejemplo de tu clase?
    lo tienes funcionando para verificarlo  (Seleccionar comentario)

  • Abraham dice:

    Me parece bien tu class. Yo hice una tb, con algunas cosas que no tiene la tuya y sin otras que me parecen interesantes.

    http://citosid.wordpress.com/2009/02/20/csession/  (Seleccionar comentario)

  • ricardo dice:

    como captura los datos de un form
    a que calse ahi k hacerle el llamado para que capture nombre de usuario y contraseña.  (Seleccionar comentario)

  • ¿Que las sesiones se guardan en el cliente? xD

    Pero mira que le gusta a la peña complicarse la vida…

    P.D: Por si no quedó claro.. NO las sesiones no se guardan en el pc del cliente. Aunque si en los servidores y estos pueden ser compartidos pero si pueden coger tu archivo de sesion, quizá también puedan coger tu clase para desencriptar los datos…

    ahm.. y la ip en los usuarios de AOL cambia por cada petición que hacen al server… SI, por cada php, html, imagen… aunque suelen mantener los dos primeros rangos…  (Seleccionar comentario)

  • Antioroku dice:

    Por lo de tu primer PD, no quiero sonar grosero, pero primero informate un poquito antes: http://www.php.net/manual/en/intro.session.php

    Y en cuanto a lo de AOL, tienes un poco de razon, pero la manera de solucionar eso, es hacerlo a travez de una mascara de IP, es cierto que cambia la IP, pero siempre se mantiene un rango.  (Seleccionar comentario)

  • vaya… veo que sigues pensando que los datos de sesion se guardan en el cliente cual cookies. Te explico… lo que se guarda en el cliente es la cookie de sesion, solo el id, no los datos en si. Porque sino no tendría sentido crear sesiones, se usarían cookies ya que precisamente tienen esa funcionalidad, aunque mermada porque no siempre funciona… Los datos de la sesion se guardan en archivos de texto, normalmente en el directorio temporal del servidor.

    Una vez hice algo similar usando un trozo del user agent del navegador y otro de la ip y era bastante seguro aunque no permitía al mismo usuario conectarse desde dos sitios diferentes al mismo tiempo… cosa que en ese caso, un mensajero instantáneo, pues no era lo mejor.

    Hoy estuve mirando oAuth a ver como lo hacían pero sigo sin verlo del todo, no lo he entendido bien, supongo que sera algo similar al ssl.  (Seleccionar comentario)

  • Diego dice:

    seria bueno amigo que pusieras un ejemplo, yo no pude hacerlo funcionar :-( . tambien seria bueno que colocaras un zip con los archivos . saludos!! y felicitaciones parece bueno lastima que ….  (Seleccionar comentario)

  • Cafe dice:

    Amigo, falta un ejemplo  (Seleccionar comentario)

RSS feed for comments on this post. TrackBack URL


Leave a Reply

Powered by WordPress | Theme: Aeros 2.0 by TheBuckmaker.com