29 de abril de 2015

Session Fixation PHP

Session fixation Session fixation (fijación de sesión) es un método de Session hijacking (robo de sesión) un poco especial, ya que, si normalmente en el robo de sesión se intenta conseguir el identificador de sesión de un usuario ya autenticado, la fijación de sesión se basa en asignar un identificador de sesión conocido a un usuario antes de que se autentique. Lo malo de este ataque es que es muy fácil de realizar. Lo bueno, es que es muy fácil de prevenir. Supongamos que la página web atacada utiliza el nombre de sesión por defecto en PHP: PHPSESSID. El atacante sólo tiene que crear un enlace a la página web objetivo de esta forma: http://www.web-atacada.com/?PHPSESSID=123456 Después, sólo tiene que hacer que el usuario al que quiere atacar utilice ese enlace para acceder a la página web. Esto se puede conseguir de varias formas: enviándole el enlace por email, a través de una red social o, incluso, sin que el usuario se dé cuenta, haciendo una redirección desde una página web del atacante. Una vez que el usuario utiliza este enlace, le está diciendo a la página web que su identificador de sesión es 123456. PHP al comprobar que no existe una sesión con ese identificador, la crea y se la asigna al usuario. Si este se autentica en el sitio, el atacante, que conoce su identificador de sesión (123456), puede hacerse con su cuenta haciéndose pasar por él. Atención: aunque la página web utilice cookies para la propagación del identificador de sesión, normalmente la configuración de PHP acepta el identificador en la URL (después lo propaga a través de cookies pero cuando ya ha creado la sesión con ese identificador). También hay que tener en cuenta que un atacante puede (con técnicas mucho más avanzadas y aprovechando errores en las directivas de seguridad de los navegadores) realizar este ataque insertando una cookie al usuario. Aunque es la forma más sencilla, este ataque no se limita sólo a la propagación por URL. MÉTODOS DE PREVENCIÓN Si bien es un ataque que se puede realizar fácilmente, la prevención es igualmente fácil. Podemos tomar las siguientes medidas: Ten cuidado donde guardas la informacion de la sesión del usuario, por omisión, PHP usa el gestor interno de almacenamiento files, el cual se establece mediante session.save_handler. Éste guarda los datos de sesión en el servidor en la ubicación especificada por la directiva de configuración session.save_path, pode defecto se encuentra en session.save_path = "c:/wamp/tmp" Utilizar únicamente cookies para la propagación del identificador de sesión. Esta medida no anula el peligro, pero hace más difícil el ataque (y muchos otros). En PHP se consigue así:
<?php
ini_set( 'session.use_only_cookies', TRUE );    
ini_set( 'session.use_trans_sid', FALSE );
?>
Tener en cuenta que el codigo anterior se debe incluir al principio de cada script php pero tambien pueden definirise en php.ini, lo que se busca es que la informacion sid del usuario no pueda obtenerse mediante $_GET['PHPSESSID'] sino solamente a traves de cookies, tambien se especifica a off el session.use_trans_sid para evitar leaking session ID en las URL’s Aparte se debe generar nuevos IDs de sesión para distintos clientes. El programa del lado del servidor nunca debería usar IDs entregados por el browser para un usuario logueado, esto es, una vez que el usuario se loguea en el sistema, éste debería generar automáticamente un nuevo ID de sesión y utilizar éste en el futuro. Desde el código que realiza el login debería utilizarse sesion_regenerate_id() a fin de no utilizar el que por defecto entrega PHP
<?php
if(($_POST['user'] == "demasiadovivo") && ($_POST['pass'] == "123456"))  
{  
    session_regenerate_id(true);  
    $_SESSION['user'] = "demasiadovivo";      
}  
?>
La función session_regenerate_id() crea un nuevo ID de sesión. De ésta forma, por más que hayamos usado un ID insertado por el atacante antes de loguearnos, una vez logueados utilizaremos un ID totalmente distinto y desconocido por el atacante. Otra opción es regenerar los identificadores de sesión para sesiones nuevas. Esto se consigue poniendo una "marca" en las sesiones de la página web. Cada vez que se hace un session_start(), si la sesión no tiene la "marca", cambiamos su identificador por uno nuevo (session_regenerate_id(true);) y desconocido para el atacante. Ejemplo:
<?php
session_start();

if (isset($_SESSION['mark']) === false)
{
 session_regenerate_id(true);
 $_SESSION['mark'] = true;
}
?>
Cambiar el identificador de sesión siempre que un usuario cambie su estado (autenticado, registrado, ...). Con la anterior medida de protección puede parecer que ya estamos protegidos, pero un atacante puede entrar en la página web, generar una sesión con "marca" y después mandar el identificador de esa sesión al usuario atacado. Así el usuario atacado acabaría con una sesión con "marca" y conocida por el atacante. Por eso, se debe regenerar el identificar de sesión siempre que un usuario realice una acción que le obligue a autenticarse. Ejemplo:
<?php
if ($user_logged_in === true)
{
 session_regenerate_id(true);
 $_SESSION['logged'] = true;
}
?>
Aviso: es muy importante que sea session_regenerate_id(true); y no session_regenerate_id(); ya que si no eliminamos la sesión antigua, estamos haciendo más fácil los ataques contra la página web al dejar múltiples sesiones válidas abiertas en el servidor.

Definir un tiempo de vida a la variable de sesión de PHP. Después de dar algunas pautas contra los ataques más usuales de robo de las sesiones, otra buena práctica es definir un tiempo de vida a las sesiones para que un usuario logueado no se quede “dormido” con la sesión abierta. En nuestra aplicación tendrá un limite de tiempo y cuando se agote se redireccionará a una página web para su nuevo acceso a la plataforma y creación de una nueva sesión. xxxxxxxx

Definir un tiempo de inactividad de session En el php.ini esta un atributo llamado 'session.cookie_lifetime', si está en 0 va a durar hasta que el navegador se cierre tambien puedes indicar cuanto debe durar la cookie de sesion, en esta línea session.gc_maxlifetime = 1440 que esta dado en segundos, cambias el 1440 por el número de segundos que estimes conveniente. Si no tienes acceso al php.ini puedes utilizar session_set_cookie_params con su parámetro respectivo que es lifetime en este caso se termina sesión cuando el usuario cierra el explorador web:
<?php
session_set_cookie_params(0);
session_start();
?>
Para que funcione debes eliminar debidamente las cookies de session cuando un usuario termine session. Este script maneja valida el tiempo establecido de vida para una sesión, en caso de que la validación haya expirado destruye la sesión y redirige al usuario al área de login para que vuelva a registrarse , el siguiente código debe colocarse en la página de inicio de sesión
<?php
session_start(); 
$_SESSION['tiempo']=time();  
?>
Este script deberá colocarse en toda página que requiera una sesión de usuario autorizada
<?php
session_start(); 
include('archivo.php'); 
..... 
..... 
?>
Y este será el contenido de archivo.php que se encargara de cerrar una sesión si es que esta ha expirado de acuerdo a un tiempo establecido por nosotros a conveniencia
<?php
$time = 3600; // una hora en mili-segundos 

// verificamos si existe la sesión 
// el nombre "session_name" es como ejemplo 
if(isset($_SESSION["session_name"])) 
{ 
      // verificamos si existe la sesión que se encarga del tiempo 
      // si existe, y el tiempo es mayor que una hora, expiramos la sesión  
      if(isset($_SESSION["expire"]) && time() > $_SESSION["expire"] + $time) 
      { 
           echo'';  
       // también puedes utilizar header(“Location:index.php”);
         
     // destruir informacion de session en el server
 
          unset($_SESSION["expire"]); 
          unset($_SESSION["session_name"]); 
         session_unset();
         session_destroy(); 

// destruir informacion de session en el cliente 
$session_cookie_params = session_get_cookie_params(); 
setcookie(session_name(), '', time() - 24 * 3600, $session_cookie_params['path'], $session_cookie_params['domain'], $session_cookie_params['secure'], $session_cookie_params['httponly']); 

// Limpiar el array $_SESSION
$_SESSION = array();  

      } 
      // creamos o redefinimos el valor de la variable de session 
      else 
      { 
           $_SESSION["expire"] = time(); 
       } 
} 
?>

Si vas a modificar el php.ini te vale esta información

session.gc_maxlifetime Este valor (por defecto 1440 segundos) define cuánto tiempo se mantendrá viva una sesión PHP sin utilizar. Por ejemplo: un usuario inicia sesión, busca a través de su aplicación o sitio web , por hora, por día. No hay problema. Mientras el tiempo entre sus clics nunca exceda 1.440 segundos. Es un valor de tiempo de espera. Colector de basura la sesión de PHP se ejecuta con una probabilidad definida por session.gc_probability dividido por session.gc_divisor . Por defecto es 1/100, lo que significa que el valor de tiempo de espera anterior se comprueba con una probabilidad de 1 en 100. Si dejas un valor de 100% es decir: gc_probability/gc_divisor = 1/10000 va a hacer que el servidor sobre-cargue a PHP indicando que en todas las peticiones haga una limpieza de las sesiones. Lo mejor en caso de ser un servidor que tiene poca visita colocarlo a que sea un 1% es decir 1/100 (también puede ser un 10% si tu servidor lo estás usando para desarrollar, es decir 1/1000). Si tiene mucha visita lo mejor es colocar a que sea 0.1% es decir 1000/1.

session.cookie_lifetime Este valor (por defecto 0, es decir, hasta el próximo reinicio del navegador) define el tiempo (en segundos) vivirá una cookie de sesión. Suena similar a session.gc_maxlifetime , pero es un enfoque completamente diferente. Este valor define indirectamente el "absoluto" tiempo de vida máximo de una sesión, si el usuario está activo o no. Si este valor se establece en 60, cada sesión termina después de una hora. Al crear mis antiguas aplicaciones en PHP, solía controlar la duración de las sesiones con únicamente un parámetro, session.gc_maxlifetime, que por defecto está ajustado a 1440 segundos (24 minutos), ya que lo cambiaba a 7200 segundos (dos horas, o 120 minutos). Aunque no es una técnica recomendada, solía modificar el parámetro en php.ini. El primer problema era que obligaba a todas las aplicaciones del servidor a permitir que las sesiones duraran 2 horas. El segundo era que una actualización en el servidor producía que la caducidad volviera a 1440 segundos. Es decir, la duración o caducidad de las sesiones no puede dejarse en manos de php.ini, sino de la propia aplicación. Podemos correr el riesgo de modificar ambos parámetros desde php.ini: session.cookie_lifetime 7200 session.gc_maxlifetime 7200 O mejor aún, colocarlos en cada página que utilice sesiones:
<?php
ini_set("session.cookie_lifetime","7200");
ini_set("session.gc_maxlifetime","7200");
session_start();
$_SESSION["ejemplo"]="invitado";
?>
Recuerda ponerlo antes de la instrucción “session_start()” y además verificar la probabilidad de gc sea 1/100 Terminar sesión apropiadamente

<?php
// destruir información de session en el server
session_unset();
session_destroy(); 

// destruir informacion de session en el cliente 
$session_cookie_params = session_get_cookie_params(); 
setcookie(session_name(), '', time() - 24 * 3600, $session_cookie_params['path'], $session_cookie_params['domain'], $session_cookie_params['secure'], $session_cookie_params['httponly']); 

// Limpiar el array $_SESSION
$_SESSION = array();  
?>