Un ataque de falsificación de petición en sitios cruzados (CSRF por sus siglas en inglés) es un tipo de vulnerabilidad de aplicaciones web en el que la víctima, sin darse cuenta, ejecuta un script en su navegador que permite aprovecharse de la sesión que hubiese iniciado en algún sitio particular. Los ataques CSRF se pueden hacer con pedidos GET o POST. Este artículo muestra cómo evitar ataques CSRF en tu aplicación web.

Pasos

Usaremos dos métodos para evitar ataques CSRF en tus pedidos GET y POST.

Lo primero que hay que hacer es un identificador aleatorio por cada pedido, una cadena única que se genera para cada sesión. Generamos el identificador y lo incluimos en cada formulario como una entrada oculta. El sistema revisará si el formulario es válido, comparando el identificador con el que está almacenado en la variable de sesión del usuario. O sea que para que un atacante pueda generar un pedido, deberá conocer el valor del identificador.

El segundo método es usar un nombre aleatorio para cada campo del formulario. El valor del nombre aleatorio de cada campo se almacena en una variable de sesión y el sistema generará un nuevo valor aleatorio después de que se haya enviado el formulario. O sea que para que funcione un ataque, el atacante tendrá que adivinar estos nombres aleatorios en los formularios.

Por ejemplo, un pedido que antes lucía así:

Ahora lucirá así:

Método 1
Método 1
Método 1 de 2:
Crea el archivo csrf.class.php

Es el archivo principal que contendrá todas las funciones que usaremos para evitar ataques CSRF.

  1. 1
    Crea csrf.class.php. Comienza creando el archivo y guardándolo con el siguiente contenido:

    <?php
    
    class csrf {
    
    Todo el código de esta sección de la guía tiene que añadirse al final de este archivo.
  2. 2
    Crea la función get_token_id().
    Esta función obtiene el identificador de la sesión del usuario. Si no había sido generada antes, se genera un identificador aleatorio nuevo.

    public function get_token_id() {
    	if(isset($_SESSION['token_id'])) { 
    		return $_SESSION['token_id'];
    	} else {
    		$token_id = $this->random(10);
    		$_SESSION['token_id'] = $token_id;
    		return $token_id;
    	}
    }
    
  3. 3
    Crea la función get_token() Function.
    Esta función obtiene el valor del identificador. Si no se generó uno antes, se generará ahora.

    public function get_token() {
    	if(isset($_SESSION['token_value'])) {
    		return $_SESSION['token_value']; 
    	} else {
    		$token = hash('sha256', $this->random(500));
    		$_SESSION['token_value'] = $token;
    		return $token;
    	}
    
    }
    
  4. 4
    Crea la función check_valid().
    Esta función se usa para revisar si el identificador y el valor del identificador son válidos. Lo hace comparando los valores del pedido GET o POST con los valores almacenados en la variable de sesión del usuario.

    public function check_valid($method) {
    	if($method == 'post' || $method == 'get') {
    		$post = $_POST;
    		$get = $_GET;
    		if(isset(${$method}[$this->get_token_id()]) && (${$method}[$this->get_token_id()] == $this->get_token())) {
    			return true;
    		} else {
    			return false;	
    		}
    	} else {
    		return false;	
    	}
    }
    


  5. 5
    Crea la función form_names().
    Esta es la segunda defensa del artículo contra el CSRF. Esta función genera nombres aleatorios para los campos de formulario.

    public function form_names($names, $regenerate) {
    	
    	$values = array();
    	foreach ($names as $n) {
    		if($regenerate == true) {
    			unset($_SESSION[$n]);
    		}
    		$s = isset($_SESSION[$n]) ? $_SESSION[$n] : $this->random(10);
    		$_SESSION[$n] = $s;
    		$values[$n] = $s;	
    	}
    	return $values;
    }
    


  6. 6
    Crea la función random().
    Esta función genera una cadena aleatoria usando el archivo aleatorio de linux, para una mayor entropía.

    private function random($len) {
    	if (@is_readable('/dev/urandom')) {
    		$f=fopen('/dev/urandom', 'r');
    		$urandom=fread($f, $len);
    		fclose($f);
    	}
    
    	$return='';
    	for ($i=0;$i<$len;++$i) {
    		if (!isset($urandom)) {
    			if ($i%2==0) mt_srand(time()%2147 * 1000000 + (double)microtime() * 1000000);
    			$rand=48+mt_rand()%64;
    		} else $rand=48+ord($urandom[$i])%64;
    
    		if ($rand>57)
    			$rand+=7;
    		if ($rand>90)
    			$rand+=6;
    
    		if ($rand==123) $rand=52;
    		if ($rand==124) $rand=53;
    		$return.=chr($rand);
    	}
    	return $return;
    }
    


  7. 7
    Cierra la llave de Clase.
    Esto finalizará la clase csrf.

    }
    
    Ahora puedes cerrar el archivo csrf.class.php, ya que terminamos de editarlo.

Método 2
Método 2
Método 2 de 2:
Protege tus páginas con csrf.class.php

Estos pasos te mostrarán como usar la clase CSRF para evitar ataques CSRF.

  1. 1
    Protege un formulario POST.
    El código que hay a continuación muestra cómo implementar la clase CSRF en un formulario.

    <?php
    session_start();
    include 'csrf.class.php';
    
    $csrf = new csrf();
    
    
    // Genera un identificador y lo valida
    $token_id = $csrf->get_token_id();
    $token_value = $csrf->get_token($token_id);
    
    // Genera nombres aleatorios para el formulario
    $form_names = $csrf->form_names(array('user', 'password'), false);
    
    
    if(isset($_POST[$form_names['user']], $_POST[$form_names['password']])) {
    	// Revisa si el identificador y su valor son válidos.
    	if($csrf->check_valid('post')) {
    		// Get the Form Variables.
    		$user = $_POST[$form_names['user']];
    		$password = $_POST[$form_names['password']];
    		
    		// La función Form va aquí
    	}
    	// Regenera un valor aleatorio nuevo para el formulario.
    	$form_names = $csrf->form_names(array('user', 'password'), true);
    }
    
    ?>
    
    <form action="index.php" method="post">
    <input type="hidden" name="<?= $token_id; ?>" value="<?= $token_value; ?>" />
    <input type="text" name="<?= $form_names['user']; ?>" /><br/>
    <input type="text" name="<?= $form_names['password']; ?>" />
    <input type="submit" value="Login"/>
    </form>
    

Acerca de este wikiHow

wikiHow es un "wiki", lo que significa que muchos de nuestros artículos están escritos por varios autores. Para crear este artículo, autores voluntarios han trabajado para editarlo y mejorarlo con el tiempo. Este artículo ha sido visto 17 829 veces.
Categorías: Programación