Estoy tratando de entender todo el problema con CSRF y las formas adecuadas de prevenirlo. (Recursos que he leído, entiendo y estoy de acuerdo con: OWASP CSRF Prevention CHeat Sheet , Preguntas sobre CSRF ).
Según tengo entendido, la vulnerabilidad en torno a CSRF se introduce por el supuesto de que (desde el punto de vista del servidor web) una cookie de sesión válida en una solicitud HTTP entrante refleja los deseos de un usuario autenticado. Pero todas las cookies para el dominio de origen se unen mágicamente a la solicitud del navegador, por lo que realmente todo el servidor puede inferir de la presencia de una cookie de sesión válida en una solicitud que la solicitud proviene de un navegador que tiene una sesión autenticada; no puede asumir nada más sobre el códigoejecutándose en ese navegador, o si realmente refleja los deseos del usuario. La forma de evitar esto es incluir información de autenticación adicional (el "token CSRF") en la solicitud, transportada por algún otro medio que no sea el manejo automático de cookies del navegador. En términos generales, la cookie de sesión autentica al usuario / navegador y el token CSRF autentica el código que se ejecuta en el navegador.
En pocas palabras, si está utilizando una cookie de sesión para autenticar a los usuarios de su aplicación web, también debe agregar un token CSRF a cada respuesta y requerir un token CSRF coincidente en cada solicitud (mutante). El token CSRF luego realiza un viaje de ida y vuelta desde el servidor al navegador de regreso al servidor, demostrando al servidor que la página que realiza la solicitud es aprobada por (generado por, incluso) ese servidor.
En cuanto a mi pregunta, que trata sobre el método de transporte específico utilizado para ese token CSRF en ese viaje de ida y vuelta.
Parece común (por ejemplo, en AngularJS , Django , Rails ) enviar el token CSRF del servidor al cliente como una cookie (es decir, en un encabezado Set-Cookie), y luego tener Javascript en el cliente para eliminarlo de la cookie y adjuntarlo como un encabezado XSRF-TOKEN separado para enviar de vuelta al servidor.
(Un método alternativo es el recomendado por , por ejemplo , Express , donde el token CSRF generado por el servidor se incluye en el cuerpo de la respuesta a través de la expansión de la plantilla del lado del servidor, adjunta directamente al código / marcado que lo devolverá al servidor, por ejemplo como una entrada de formulario oculta. Ese ejemplo es una forma más web 1.0-ish de hacer las cosas, pero generalizaría bien a un cliente más pesado de JS).
¿Por qué es tan común usar Set-Cookie como transporte descendente para el token CSRF / por qué es una buena idea? Me imagino que los autores de todos estos marcos consideraron sus opciones cuidadosamente y no se equivocaron. Pero a primera vista, el uso de cookies para evitar lo que es esencialmente una limitación de diseño de las cookies parece una tontería. De hecho, si utilizó cookies como transporte de ida y vuelta (Set-Cookie: encabezado aguas abajo para que el servidor le diga al navegador el token CSRF, y Cookie: encabezado aguas arriba para que el navegador lo devuelva al servidor) volvería a introducir la vulnerabilidad están tratando de arreglarlo
Me doy cuenta de que los marcos anteriores no usan cookies para todo el viaje de ida y vuelta para el token CSRF; usan Set-Cookie en sentido descendente, luego algo más (por ejemplo, un encabezado X-CSRF-Token) en sentido ascendente, y esto elimina la vulnerabilidad. Pero incluso el uso de Set-Cookie como transporte descendente es potencialmente engañoso y peligroso; el navegador ahora adjuntará el token CSRF a cada solicitud, incluidas las solicitudes XSRF maliciosas genuinas; en el mejor de los casos, eso hace que la solicitud sea más grande de lo que debe ser y, en el peor de los casos, un código de servidor bien intencionado pero equivocado podría intentar usarlo, lo que sería realmente malo. Y además, dado que el destinatario real del token CSRF es Javascript del lado del cliente, eso significa que esta cookie no se puede proteger con http-only.
Respuestas:
Una buena razón, que ya mencionó, es que una vez que se recibe la cookie CSRF, está disponible para su uso en toda la aplicación en el script del cliente para su uso en formularios regulares y POST AJAX. Esto tendrá sentido en una aplicación pesada de JavaScript, como la empleada por AngularJS (el uso de AngularJS no requiere que la aplicación sea una aplicación de una sola página, por lo que sería útil donde el estado necesita fluir entre diferentes solicitudes de página donde el valor CSRF normalmente no puede persistir en el navegador).
Considere los siguientes escenarios y procesos en una aplicación típica para algunos pros y contras de cada enfoque que describa. Estos se basan en el patrón de token de sincronizador .
Enfoque del cuerpo de solicitud
Ventajas:
Desventajas
Encabezado HTTP personalizado (aguas abajo)
Ventajas:
Desventajas
Encabezado HTTP personalizado (ascendente)
Ventajas:
Desventajas
Encabezado HTTP personalizado (ascendente y descendente)
Ventajas:
Desventajas
Set-Cookie
Ventajas:
Desventajas
Por lo tanto, el enfoque de cookies es bastante dinámico y ofrece una manera fácil de recuperar el valor de la cookie (cualquier solicitud HTTP) y usarla (JS puede agregar el valor a cualquier formulario automáticamente y puede emplearse en solicitudes AJAX como encabezado o como valor de forma). Una vez que se ha recibido el token CSRF para la sesión, no hay necesidad de regenerarlo ya que un atacante que emplea un exploit CSRF no tiene ningún método para recuperar este token. Si un usuario malintencionado intenta leer el token CSRF del usuario en cualquiera de los métodos anteriores, esto se evitará mediante la Política del mismo origen . Si un usuario malintencionado intenta recuperar el lado del servidor de token CSRF (por ejemplo, a través de
curl
), entonces este token no se asociará a la misma cuenta de usuario ya que la cookie de sesión de autenticación de la víctima no se incluirá en la solicitud (sería del atacante, por lo tanto, no se asociará al servidor con la sesión de la víctima).Además del patrón de token de sincronizador, también existe la cookie de envío dobleMétodo de prevención CSRF, que por supuesto utiliza cookies para almacenar un tipo de token CSRF. Esto es más fácil de implementar ya que no requiere ningún estado del lado del servidor para el token CSRF. De hecho, el token CSRF podría ser la cookie de autenticación estándar cuando se utiliza este método, y este valor se envía a través de las cookies como de costumbre con la solicitud, pero el valor también se repite en un campo oculto o en un encabezado, del cual un atacante no puede replicarse como no pueden leer el valor en primer lugar. Sin embargo, se recomienda elegir otra cookie, que no sea la cookie de autenticación, de modo que la cookie de autenticación se pueda asegurar marcando HttpOnly. Entonces, esta es otra razón común por la que encontraría la prevención CSRF utilizando un método basado en cookies.
fuente
El uso de una cookie para proporcionar el token CSRF al cliente no permite un ataque exitoso porque el atacante no puede leer el valor de la cookie y, por lo tanto, no puede colocarla donde la validación CSRF del lado del servidor lo requiere.
El atacante podrá causar una solicitud al servidor con la cookie de token de autenticación y la cookie CSRF en los encabezados de solicitud. Pero el servidor no está buscando el token CSRF como una cookie en los encabezados de las solicitudes, está buscando en la carga útil de la solicitud. E incluso si el atacante sabe dónde colocar el token CSRF en la carga útil, tendrían que leer su valor para colocarlo allí. Pero la política de origen cruzado del navegador impide leer cualquier valor de cookie del sitio web de destino.
La misma lógica no se aplica a la cookie de token de autenticación, porque el servidor la espera en los encabezados de solicitud y el atacante no tiene que hacer nada especial para colocarla allí.
fuente
src='bank.com/transfer?to=hacker&amount=1000
el que solicitará el navegador, completa con las cookies asociadas para ese sitio (bank.com
)?Mi mejor conjetura en cuanto a la respuesta: considere estas 3 opciones para obtener el token CSRF del servidor al navegador.
Creo que el primero, el cuerpo de la solicitud (aunque lo demostró el tutorial Express que vinculé en la pregunta ), no es tan portátil para una amplia variedad de situaciones; no todos generan dinámicamente todas las respuestas HTTP; donde terminas necesitando poner el token en la respuesta generada puede variar ampliamente (en una entrada de forma oculta; en un fragmento de código JS o una variable accesible por otro código JS; tal vez incluso en una URL, aunque generalmente parece un mal lugar para poner tokens CSRF). Por lo tanto, mientras se puede trabajar con cierta personalización, # 1 es un lugar difícil para hacer un enfoque único para todos.
El segundo, el encabezado personalizado, es atractivo pero en realidad no funciona, porque si bien JS puede obtener los encabezados de un XHR que invocó, no puede obtener los encabezados de la página desde la que se cargó .
Eso deja al tercero, una cookie transportada por un encabezado Set-Cookie, como un enfoque que es fácil de usar en todas las situaciones (el servidor de cualquier persona podrá establecer encabezados de cookies por solicitud, y no importa qué tipo de los datos están en el cuerpo de la solicitud). Entonces, a pesar de sus desventajas, fue el método más fácil para que los marcos se implementen ampliamente.
fuente
Además de la cookie de sesión (que es una especie de estándar), no quiero usar cookies adicionales.
Encontré una solución que funciona para mí al crear una aplicación web de página única (SPA), con muchas solicitudes AJAX. Nota: Estoy usando Java del lado del servidor y JQuery del lado del cliente, pero no hay cosas mágicas, así que creo que este principio se puede implementar en todos los lenguajes de programación populares.
Mi solución sin cookies adicionales es simple:
Lado del cliente
Almacene el token CSRF que devuelve el servidor después de un inicio de sesión exitoso en una variable global (si quiere usar almacenamiento web en lugar de un global, eso está bien, por supuesto). Indique a JQuery que proporcione un encabezado X-CSRF-TOKEN en cada llamada AJAX.
La página principal de "índice" contiene este fragmento de JavaScript:
Lado del servidor
En el inicio de sesión exitoso, cree un token CSRF aleatorio (y lo suficientemente largo), almacénelo en la sesión del lado del servidor y devuélvalo al cliente. Filtre ciertas solicitudes entrantes (confidenciales) comparando el valor del encabezado X-CSRF-TOKEN con el valor almacenado en la sesión: estos deben coincidir.
Las llamadas AJAX sensibles (datos de formulario POST y datos JET GET), y el filtro del lado del servidor que las atrapa, están bajo una ruta / dataservice / *. Las solicitudes de inicio de sesión no deben golpear el filtro, por lo que están en otra ruta. Las solicitudes de recursos HTML, CSS, JS e imágenes tampoco están en la ruta / dataservice / *, por lo tanto, no se filtran. Estos no contienen nada secreto y no pueden hacer daño, por lo que está bien.
fuente