¿Existe alguna técnica efectiva de prevención del lado del servidor contra CSRF asistida por PHP?

4

Empezaré por decirlo: soy simplemente un entusiasta de la ciberseguridad, no un experto. Por lo tanto, voy a decir lo que creo que sé hasta ahora, no dude en corregirme en cualquier momento.

A través de mis lecturas, he venido a aprender sobre:

  • La existencia de CSRF
  • Lo básico detrás de esto, para las solicitudes GET y POST
  • Token y Referer técnicas de mitigación
  • Cómo se puede recuperar el Token si el sitio web de destino tiene una vulnerabilidad XSS
  • El hecho de que el remitente se pasa en texto sin cifrar a través de los encabezados HTTP y, por lo tanto, se puede falsificar
  • cURL utilizado para cargar la página primero, obtener el token y usar este para enviar los datos de POST y obtener los contenidos de la página posteriores a la autenticación

Por lo tanto, mi pregunta es la siguiente: si alguien usa un script PHP que carga la página que alberga el formulario POST, obtiene el token generado aleatoriamente y envía los datos POST con un encabezado "Referer" personalizado que coincide con el sitio web de destino, es ¿Hay algo que pueda hacer en mi servidor para mitigar esto?

    
pregunta MadWard 23.05.2016 - 17:06
fuente

2 respuestas

3

Creo que estás malinterpretando cómo funciona un ataque CSRF, y por qué los tokens CSRF protegen contra ellos. Así que comencemos con cómo funciona uno.

El atacante engaña a la víctima para que visite http://evil.com que contiene un formulario que automáticamente realiza un POST a http://bank.com/transfer?to=evilHacker&amount=1000000 . Si la víctima ya ha iniciado sesión en su banco, el navegador enviará la cookie con el ID de sesión como de costumbre y no hay forma de que el servidor bancario sepa que la víctima no tenía la intención de transferir el dinero. Tenga en cuenta que esto se basa en que el navegador envíe la cookie con el ID de sesión a bank.com .

Entonces, ¿cómo puede ayudar un token CSRF? Si el bank.com siempre comprueba que hay un token aleatorio único para esa sesión en la solicitud, evil.com no puede falsificar la solicitud ya que el pirata informático malvado que ejecuta ese sitio no sabe qué es el token.

La comprobación de referencia funciona de manera similar. La solicitud realizada por el navegador desde el sitio evil.com tendría evil.com y no bank.com como referente. Los navegadores no permiten la modificación del encabezado del remitente, por lo que el atacante no puede falsificar eso.

Entonces, ¿qué pasa con su línea de ataque: que el atacante solicite el sitio con un script PHP en su servidor para obtener el token de CSRF? Esto no funcionará, porque el atacante no obtendrá el token CSRF de las víctimas. El token es único para la sesión y, por lo tanto, la cookie con el ID de sesión. Pero los atacantes no saben qué ID de sesión tiene la víctima, por lo que no puede incluirla en la solicitud.

Observe la diferencia en el primer escenario, donde el navegador incluyó la cookie. Eso no puede ocurrir en el servidor.

    
respondido por el Anders 23.05.2016 - 17:33
fuente
0

La respuesta de Anders responde a la pregunta, pero solo ofrece la solución de almacenar una copia del token en la sesión. También sería posible almacenar el lado del cliente del token siempre que esté debidamente protegido contra los ataques de repetición, pero esto tiene una importante superposición con el problema del secuestro de sesión ...

if ($_REQUEST['token']) {
   if (validate($_REQUEST['token'], $_COOKIE['ctoken'])) {
     // do the protected thing....
   } else {
     // handle exception
   }
}
$token=sha1(openssl_random_pseudo_bytes(40)) . ":" . time();
$client_stored_token=encrypt($token, create_key);
set_cookie('ctoken',$client_stored_token);

function validate($token, $client_stored_token) 
{
    $client_token=decrypt($client_stored_token, create_key());
    @list($rand_bytes, $timestamp)=explode(':', $client_token);
    if ($rand_bytes==$token && time()-$timestamp<MAX_THRESHOLD) {
       return true;
    } 
    return false;
}
function create_key()
{
   return sha1($_SERVER['HTTP_USER_AGENT'] 
      .(int)(ip2long($_SERVER['REMOTE_ADDR'])/65536)
      .SOME_STATIC_SALT);
}

(también puede ser posible propagar el token a través de window.name, pero eso necesita un poco más de reflexión).

Anders señaló que:

  

Los navegadores no permiten la modificación del encabezado del remitente, por lo que el atacante no puede falsificar eso.

Lamentablemente, hay varios problemas con el encabezado del remitente en varios navegadores. Si te apoyas en eso, no te atascarás.

  

El hecho de que el remitente se pasa en texto sin cifrar a través de los encabezados HTTP y, por lo tanto, podría ser falsificado

Incluso los mecanismos de protección de sesión y CSRF más sofisticados son fácilmente derrotados por un MITM en una conexión HTTP. Es por eso que usamos HTTPS.

    
respondido por el symcbean 23.06.2016 - 13:45
fuente

Lea otras preguntas en las etiquetas