Estoy escribiendo una aplicación (Django, sucede) y solo quiero tener una idea de qué es realmente un "token CSRF" y cómo protege los datos. ¿Los datos de publicación no son seguros si no usa tokens CSRF?
Estoy escribiendo una aplicación (Django, sucede) y solo quiero tener una idea de qué es realmente un "token CSRF" y cómo protege los datos. ¿Los datos de publicación no son seguros si no usa tokens CSRF?
Respuestas:
www.mybank.com
mybank.com
dará como resultado una solicitud (conceptual) del formulario http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>
. (Su número de cuenta no es necesario porque está implícito en su inicio de sesión).www.cute-cat-pictures.org
, sin saber que es un sitio malicioso.mybank.com
(¡requiere suerte!), Podría incluir en su página una solicitud como http://www.mybank.com/transfer?to=123456;amount=10000
(¿dónde 123456
está el número de su cuenta en las Islas Caimán? y 10000
es una cantidad que antes creías que te alegraba poseer).www.cute-cat-pictures.org
la página, por lo que su navegador hará que esa solicitud.www.mybank.com
cookie y se verá perfectamente legítima. Ahí va tu dinero!Este es el mundo sin tokens CSRF .
Ahora para el mejor con tokens CSRF :
http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971
.mybank.com
se incluirá en su propia página web cuando se lo sirvan. Es diferente cada vez que sirven cualquier página a alguien.www.mybank.com
.Resultado: Mantiene sus 10000
unidades monetarias. Le sugiero que done algo de eso a Wikipedia.
(Su experiencia puede ser diferente.)
EDITAR del comentario que vale la pena leer:
Sería digno de notar que el script de www.cute-cat-pictures.org
normalmente no tiene acceso a su token anti-CSRF www.mybank.com
debido al control de acceso HTTP. Esta nota es importante para algunas personas que envían un encabezado Access-Control-Allow-Origin: *
sin razón para cada respuesta del sitio web sin saber para qué sirve, simplemente porque no pueden usar la API desde otro sitio web.
www.cute-cat-pictures.org
normalmente no tiene acceso a su token anti-CSRF www.mybank.com
debido al control de acceso HTTP. Esta nota es importante para algunas personas que envían un encabezado Access-Control-Allow-Origin: *
sin razón para cada respuesta del sitio web sin saber para qué sirve, simplemente porque no pueden usar la API desde otro sitio web.
Sí, los datos de publicación son seguros. Pero el origen de esos datos no lo es. De esta manera, alguien puede engañar al usuario con JS para que inicie sesión en su sitio, mientras navega por la página web del atacante.
Para evitar eso, django enviará una clave aleatoria tanto en la cookie como en los datos del formulario. Luego, cuando los usuarios POST, verificará si dos claves son idénticas. En caso de que el usuario sea engañado, el sitio web de terceros no puede obtener las cookies de su sitio, lo que provoca un error de autenticación.
El sitio genera un token único cuando hace la página del formulario. Este token es necesario para publicar / recuperar datos en el servidor.
Dado que su sitio genera el token y se proporciona solo cuando se genera la página con el formulario, algún otro sitio no puede imitar sus formularios; no tendrán el token y, por lo tanto, no podrán publicar en su sitio.
El blog Cloud Under tiene una buena explicación de los tokens CSRF.
Imagine que tiene un sitio web como un Twitter simplificado, alojado en a.com. Los usuarios registrados pueden ingresar texto (un tweet) en un formulario que se envía al servidor como una solicitud POST y se publica cuando presionan el botón Enviar. En el servidor, el usuario se identifica mediante una cookie que contiene su ID de sesión única, por lo que su servidor sabe quién publicó el Tweet.
La forma podría ser tan simple como eso:
<form action="http://a.com/tweet" method="POST"> <input type="text" name="tweet"> <input type="submit"> </form>
Ahora imagine que un chico malo copia y pega este formulario en su sitio web malicioso, digamos b.com. La forma aún funcionaría. Siempre que un usuario haya iniciado sesión en su Twitter (es decir, que tenga una cookie de sesión válida para a.com), la solicitud POST se enviará
http://a.com/tweet
y procesará como de costumbre cuando el usuario haga clic en el botón Enviar.Hasta ahora, este no es un gran problema, siempre y cuando el usuario esté al tanto de lo que hace exactamente el formulario, pero qué pasa si nuestro chico malo modifica el formulario de esta manera:
<form action="https://example.com/tweet" method="POST"> <input type="hidden" name="tweet" value="Buy great products at http://b.com/#iambad"> <input type="submit" value="Click to win!"> </form>
Ahora, si uno de tus usuarios termina en el sitio web del malo y presiona el botón "¡Haz clic para ganar!" botón, el formulario se envía a su sitio web, el usuario se identifica correctamente mediante la ID de sesión en la cookie y se publica el Tweet oculto.
Si nuestro chico malo fuera aún peor, haría que el usuario inocente envíe este formulario tan pronto como abra su página web usando JavaScript, tal vez incluso completamente oculto en un iframe invisible. Esto es básicamente una falsificación de solicitud entre sitios.
Un formulario se puede enviar fácilmente desde cualquier lugar a todas partes. En general, esa es una característica común, pero hay muchos más casos en los que es importante permitir que solo se envíe un formulario desde el dominio al que pertenece.
Las cosas son aún peores si su aplicación web no distingue entre solicitudes POST y GET (por ejemplo, en PHP usando $ _REQUEST en lugar de $ _POST). ¡No hagas eso! Las solicitudes de modificación de datos se pueden enviar tan fácilmente como
<img src="http://a.com/tweet?tweet=This+is+really+bad">
integrarse en un sitio web malicioso o incluso en un correo electrónico.¿Cómo me aseguro de que un formulario solo se pueda enviar desde mi propio sitio web? Aquí es donde entra en juego el token CSRF. Un token CSRF es una cadena aleatoria, difícil de adivinar. En una página con un formulario que desea proteger, el servidor generará una cadena aleatoria, el token CSRF, lo agregará al formulario como un campo oculto y también lo recordará de alguna manera, ya sea almacenándolo en la sesión o configurando una cookie que contiene el valor Ahora el formulario se vería así:
<form action="https://example.com/tweet" method="POST"> <input type="hidden" name="csrf-token" value="nc98P987bcpncYhoadjoiydc9ajDlcn"> <input type="text" name="tweet"> <input type="submit"> </form>
Cuando el usuario envía el formulario, el servidor simplemente tiene que comparar el valor del campo csrf-token publicado (el nombre no importa) con el token CSRF recordado por el servidor. Si ambas cadenas son iguales, el servidor puede continuar procesando el formulario. De lo contrario, el servidor debería detener inmediatamente el procesamiento del formulario y responder con un error.
¿Por qué funciona esto? Hay varias razones por las cuales el malo de nuestro ejemplo anterior no puede obtener el token CSRF:
Copiar el código fuente estático de nuestra página a un sitio web diferente sería inútil, porque el valor del campo oculto cambia con cada usuario. Sin que el sitio web del malo conozca el token CSRF del usuario actual, su servidor siempre rechazará la solicitud POST.
Debido a que la página maliciosa del malo es cargada por el navegador de su usuario desde un dominio diferente (b.com en lugar de a.com), el malo no tiene la posibilidad de codificar un JavaScript, que carga el contenido y, por lo tanto, el token CSRF actual de nuestro usuario su página web. Esto se debe a que los navegadores web no permiten solicitudes AJAX entre dominios de forma predeterminada.
El malo tampoco puede acceder a la cookie establecida por su servidor, porque los dominios no coincidirían.
¿Cuándo debo proteger contra la falsificación de solicitudes entre sitios? Si puede asegurarse de no mezclar GET, POST y otros métodos de solicitud como se describe anteriormente, un buen comienzo sería proteger todas las solicitudes POST de forma predeterminada.
No tiene que proteger las solicitudes PUT y DELETE, porque como se explicó anteriormente, un navegador no puede enviar un formulario HTML estándar utilizando esos métodos.
JavaScript, por otro lado, puede hacer otros tipos de solicitudes, por ejemplo, utilizando la función $ .ajax () de jQuery, pero recuerde, para que las solicitudes AJAX funcionen, los dominios deben coincidir (siempre y cuando no configure explícitamente su servidor web) .
Esto significa que, a menudo, ni siquiera tiene que agregar un token CSRF a las solicitudes AJAX, incluso si son solicitudes POST, pero tendrá que asegurarse de que solo omita la verificación CSRF en su aplicación web si la solicitud POST es realmente un Solicitud AJAX. Puede hacerlo buscando la presencia de un encabezado como X-Requested-With, que las solicitudes de AJAX generalmente incluyen. También puede establecer otro encabezado personalizado y verificar su presencia en el lado del servidor. Eso es seguro, ya que un navegador no agregaría encabezados personalizados a un envío de formulario HTML normal (ver arriba), por lo que no es posible que el Sr. Bad Guy simule este comportamiento con un formulario.
Si tiene dudas acerca de las solicitudes AJAX, porque por alguna razón no puede verificar un encabezado como X-Requested-With, simplemente pase el token CSRF generado a su JavaScript y agregue el token a la solicitud AJAX. Hay varias maneras de hacer esto; ya sea agregarlo a la carga útil como lo haría un formulario HTML normal, o agregar un encabezado personalizado a la solicitud AJAX. Siempre que su servidor sepa dónde buscarlo en una solicitud entrante y pueda compararlo con el valor original que recuerda de la sesión o cookie, estará ordenado.
La raíz de todo es asegurarse de que las solicitudes provienen de los usuarios reales del sitio. Se genera un token csrf para los formularios y debe estar vinculado a las sesiones del usuario. Se utiliza para enviar solicitudes al servidor, en el que el token las valida. Esta es una forma de protección contra csrf, otra sería verificar el encabezado de referencia.