ruby-on-rails - tutorial - ruby on rails que es
Entendiendo el token de autenticidad de Rails (10)
Me estoy topando con algunos problemas relacionados con el token de autenticidad en Rails, como lo he hecho muchas veces.
Pero realmente no quiero solo resolver este problema y continuar. Realmente me gustaría entender el token de autenticidad. Bueno, mi pregunta es, ¿tiene alguna fuente completa de información sobre este tema o pasaría su tiempo para explicar en detalles aquí?
¿Qué es CSRF?
El token de autenticidad es una contramedida para la falsificación de solicitudes entre sitios (CSRF). ¿Qué es CSRF, te preguntarás?
Es una forma en la que un atacante puede potencialmente secuestrar sesiones sin siquiera conocer tokens de sesión.
Escenario :
- Visite el sitio de su banco, inicie sesión.
- Luego visite el sitio del atacante (por ejemplo, un anuncio patrocinado de una organización no confiable).
- La página del atacante incluye un formulario con los mismos campos que el formulario "Transferir fondos" del banco.
- El atacante conoce la información de su cuenta y tiene campos de formulario rellenados previamente para transferir dinero de su cuenta a la cuenta del atacante.
- La página del atacante incluye Javascript que envía el formulario a su banco.
- Cuando se envía el formulario, el navegador incluye sus cookies para el sitio del banco, incluido el token de sesión.
- El banco transfiere dinero a la cuenta del atacante.
- La forma puede estar en un iframe que es invisible, por lo que nunca se sabe que ocurrió el ataque.
- Esto se denomina falsificación de solicitud entre sitios (CSRF).
Solución CSRF :
- El servidor puede marcar los formularios que vinieron del propio servidor
- Cada formulario debe contener un token de autenticación adicional como un campo oculto.
- El token debe ser impredecible (el atacante no puede adivinarlo).
- Servidor proporciona token válido en formularios en sus páginas.
- El servidor comprueba el token cuando se publica el formulario, rechaza los formularios sin el token adecuado.
- Ejemplo token: identificador de sesión cifrado con clave secreta del servidor.
- Rails genera automáticamente dichos tokens: vea el campo de entrada authenticity_token en cada formulario.
El Authenticity Token
es el método de Rails para prevent ataques de falsificación de solicitudes entre sitios (CSRF o XSRF) .
Para simplificarlo, se asegura de que las solicitudes PUT / POST / DELETE (métodos que pueden modificar el contenido) a su aplicación web se realicen desde el navegador del cliente y no desde un tercero (un atacante) que tenga acceso a una cookie creada. en el lado del cliente.
El token de autenticidad está diseñado para que sepa que su formulario se envía desde su sitio web. Se genera desde la máquina en la que se ejecuta con un identificador único que solo su máquina puede conocer, lo que ayuda a prevenir ataques de falsificación de solicitudes entre sitios.
Si simplemente tiene dificultades con los rieles que le niegan el acceso a su script AJAX, puede usar
<%= form_authenticity_token %>
para generar el token correcto cuando está creando su formulario.
Puedes leer más sobre esto en la documentation .
El token de autenticidad se usa para prevenir ataques de falsificación de solicitudes entre sitios (CSRF). Para comprender el token de autenticidad, primero debe comprender los ataques CSRF.
CSRF
Supongamos que usted es el autor de bank.com
. Tiene un formulario en su sitio que se utiliza para transferir dinero a una cuenta diferente con una solicitud GET:
Un pirata informático podría simplemente enviar una solicitud HTTP al servidor que diga GET /transfer?amount=$1000000&account-to=999999
, ¿verdad?
Incorrecto. El ataque de los hackers no funcionará. El servidor básicamente pensará?
Eh ¿Quién es este chico tratando de iniciar una transferencia. No es el dueño de la cuenta, eso es seguro.
¿Cómo sabe esto el servidor? Porque no hay una cookie session_id
autentique al solicitante.
Cuando inicia sesión con su nombre de usuario y contraseña, el servidor establece una cookie de session_id
en su navegador. De esa manera, no tiene que autenticar cada solicitud con su nombre de usuario y contraseña. Cuando su navegador envía la cookie session_id
, el servidor sabe:
Oh, ese es John Doe. Se ha registrado con éxito hace 2.5 minutos. Él es bueno para ir
Un hacker podría pensar:
Hmm Una solicitud HTTP normal no funcionará, pero si pudiera obtener esa cookie de
session_id
, estaría dorado.
El navegador de los usuarios tiene un conjunto de cookies para el dominio bank.com
. Cada vez que el usuario realiza una solicitud al dominio bank.com
, todas las cookies se envían. Incluyendo la cookie session_id
.
Por lo tanto, si un pirata informático pudiera hacer que realices la solicitud GET que transfiere dinero a su cuenta, tendrá éxito. ¿Cómo podría engañarte para que lo hicieras? Con Cross Site Request Forgery.
Es bastante simple, en realidad. El hacker podría conseguir que visites su sitio web. En su sitio web, podría tener la siguiente etiqueta de imagen:
<img src="http://bank.com/transfer?amount=$1000000&account-to=999999">
Cuando el navegador de los usuarios encuentre esa etiqueta de imagen, hará una solicitud GET a esa URL. Y dado que la solicitud proviene de su navegador, le enviará todas las cookies asociadas con bank.com
. Si el usuario ha bank.com
recientemente en bank.com
... se bank.com
la cookie session_id
, y el servidor pensará que el usuario tenía la intención de transferir $ 1,000,000 a la cuenta 999999.
Bueno, no visites sitios peligrosos y estarás bien.
Eso no es suficiente. ¿Qué pasa si alguien publica esa imagen en Facebook y aparece en tu muro? ¿Qué sucede si se inyecta en un sitio que está visitando con un ataque XSS?
No es tan malo. Sólo las solicitudes GET son vulnerables.
No es verdad. Un formulario que envía una solicitud POST puede generarse dinámicamente. Aquí está el ejemplo de la Guía de Rails sobre Seguridad :
<a href="http://www.harmless.com/" onclick="
var f = document.createElement(''form'');
f.style.display = ''none'';
this.parentNode.appendChild(f);
f.method = ''POST'';
f.action = ''http://www.example.com/account/destroy'';
f.submit();
return false;">To the harmless survey</a>
Token de Autenticidad
Cuando su ApplicationController
tiene esto:
protect_from_forgery with: :exception
Esta:
<%= form_tag do %>
Form contents
<% end %>
Se compila en esto:
<form accept-charset="UTF-8" action="/" method="post">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
Form contents
</form>
En particular, se genera lo siguiente:
<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
Para protegerse contra los ataques CSRF, si Rails no ve el token de autenticidad enviado junto con una solicitud, no considerará la solicitud como segura.
¿Cómo se supone que un atacante sabe qué es este token? Se genera aleatoriamente un valor diferente cada vez que se genera el formulario:
Un ataque de Cross Site Scripting (XSS): así es como. Pero esa es una vulnerabilidad diferente para un día diferente.
Tenga en cuenta que el mecanismo del token de autenticidad puede generar condiciones de carrera si tiene varias solicitudes simultáneas del mismo cliente. En esta situación, su servidor puede generar múltiples tokens de autenticidad cuando solo debería haber uno, y el cliente que recibe el token anterior en un formulario fallará en su próxima solicitud porque el token de cookie de sesión se ha sobrescrito. Hay un informe sobre este problema y una solución no completamente trivial aquí: http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/
ya que el Authenticity Token
es tan importante, y en Rails 3.0+ puede usar
<%= token_tag nil %>
crear
<input name="authenticity_token" type="hidden" value="token_value">
en cualquier sitio
¿Qué es un identificador de autenticación?
Esta es una cadena aleatoria utilizada por la aplicación Rails para asegurarse de que el usuario está solicitando o realizando una acción desde la página de la aplicación, no desde otra aplicación o sitio.
¿Por qué es necesaria una autenticación?
Para proteger su aplicación o sitio de la falsificación de solicitudes entre sitios.
¿Cómo agregar un identificador de autenticación a un formulario?
Si está generando un formulario usando la etiqueta form_for, se agrega automáticamente un símbolo de autenticación; de lo contrario, puede usar <%= csrf_meta_tag %>
.
Ejemplo de ataque mínimo que se evitaría.
En mi sitio web evil.com
te convenzo para que envíes el siguiente formulario:
<form action="http://bank.com/transfer" method="post">
<p><input type="hidden" name="to" value="ciro"></p>
<p><input type="hidden" name="ammount" value="100"></p>
<p><button type="submit">CLICK TO GET PRIZE!!!</button></p>
</form>
Si ha iniciado sesión en su banco a través de cookies de sesión, entonces las cookies se enviarán y la transferencia se realizará sin que usted lo sepa.
Es decir, cuando el token CSRF entra en juego:
- con la respuesta GET que devolvió el formulario, Rails envía un parámetro oculto aleatorio muy largo
- cuando el navegador realiza la solicitud POST, enviará el parámetro y el servidor solo lo aceptará si coincide
Así que el formulario en un navegador auténtico se vería así:
<form action="http://bank.com/transfer" method="post">
<p><input type="hidden" name="authenticity_token" value="j/DcoJ2VZvr7vdf8CHKsvjdlDbmiizaOb5B8DMALg6s=" ></p>
<p><input type="hidden" name="to" value="ciro"></p>
<p><input type="hidden" name="ammount" value="100"></p>
<p><button type="submit">Send 100$ to Ciro.</button></p>
</form>
Por lo tanto, mi ataque fallaría, ya que no estaba enviando el parámetro authenticity_token
, y no hay forma de que pudiera adivinarlo ya que es un número aleatorio enorme.
Esta técnica de prevención se llama patrón de token de sincronizador .
El patrón de token del sincronizador funciona debido a la Política del mismo origen : si pudiera hacer una solicitud GET de XHR a su banco desde evil.com
, y leer el resultado, podría leer un token y luego realizar la solicitud más adelante. He explicado esto más detalladamente en: https://security.stackexchange.com/a/72569/53321
Le recomiendo que lea la guía OWASP , sobre este y cualquier otro asunto de seguridad.
Cómo Rails envía los tokens.
Cubierto en: Rieles: ¿Cómo funciona csrf_meta_tag?
Básicamente:
Los ayudantes de HTML como
form_tag
agregan un campo oculto al formulario si no es un formulario GETAJAX es tratado automáticamente por jquery-ujs , que lee el token de los
meta
elementos agregados a su encabezado porcsrf_meta_tags
(presente en la plantilla predeterminada), y lo agrega a cualquier solicitud realizada.uJS también intenta actualizar el token en formularios en fragmentos en caché desactualizados.
Otros enfoques de prevención.
- compruebe si ciertos encabezados están presentes, por ejemplo, con
X-Requested-With
:- ¿Cuál es el punto del encabezado X-Requested-With?
- https://security.stackexchange.com/questions/23371/csrf-protection-with-custom-headers-and-without-validating-token
- ¿Es suficiente una comprobación del servidor de cabecera X-Requested-With para proteger contra un CSRF para una aplicación controlada por ajax?
- verifique el valor del encabezado de
Origin
: https://security.stackexchange.com/questions/91165/why-is-the-synchronizer-token-pattern-preferred-over-the-origin-header-check-to - re-autenticación: pida al usuario nuevamente la contraseña. Esto se debe hacer para cada operación crítica (inicio de sesión en el banco y transferencias de dinero, cambios de contraseña en la mayoría de los sitios web), en caso de que su sitio reciba XSSed. El inconveniente es que el usuario tiene que escribir la contraseña varias veces, lo que es aburrido y aumenta las posibilidades de keylogging / hombro surf.
Lo que pasa
Cuando el usuario ve un formulario para crear, actualizar o destruir un recurso, la aplicación Rails crea un identificador de authenticity_token
aleatorio, almacena este token en la sesión y lo coloca en un campo oculto en el formulario. Cuando el usuario envía el formulario, Rails busca el identificador de authenticity_token
, lo compara con el almacenado en la sesión y, si coinciden, se permite que la solicitud continúe.
Por que sucede
Dado que el token de autenticidad se almacena en la sesión, el cliente no puede saber su valor. Esto evita que las personas envíen formularios a una aplicación de Rails sin ver el formulario dentro de esa aplicación. Imagine que está utilizando el servicio A, inició sesión en el servicio y todo está bien. Ahora imagine que fue a usar el servicio B, vio una imagen que le gusta y presionó la imagen para verla en mayor tamaño. Ahora, si había algún código malvado en el servicio B, podría enviar una solicitud al servicio A (en el que está conectado) y solicitar la eliminación de su cuenta, enviando una solicitud a http://serviceA.com/close_account
. Esto es lo que se conoce como CSRF (falsificación de solicitud entre sitios) .
Si el servicio A está utilizando tokens de autenticidad, este vector de ataque ya no es aplicable, ya que la solicitud del servicio B no contendría el token de autenticidad correcto y no se le permitirá continuar.
Los documentos API describen los detalles de la metaetiqueta:
La protección CSRF se activa con el método
protect_from_forgery
, que comprueba el token y restablece la sesión si no coincide con lo que se esperaba. De forma predeterminada, se genera una llamada a este método para las nuevas aplicaciones de Rails. El parámetro token se llamaauthenticity_token
de forma predeterminada. El nombre y el valor de este token deben agregarse a cada diseño que represente formularios incluyendocsrf_meta_tags
en el encabezado HTML.
Notas
Tenga en cuenta que Rails solo verifica los métodos no idempotentes (POST, PUT / PATCH y DELETE). La solicitud GET no se comprueba para el token de autenticidad. ¿Por qué? porque la especificación HTTP establece que las solicitudes GET son idempotentes y no deben crear, alterar o destruir recursos en el servidor, y la solicitud debe ser idempotentes (si ejecuta el mismo comando varias veces, siempre debe obtener el mismo resultado).
Además, la implementación real es un poco más complicada como se definió al principio, lo que garantiza una mejor seguridad. Rails no emite el mismo token almacenado con cada formulario. Tampoco genera y almacena un token diferente cada vez. Genera y almacena un hash criptográfico en una sesión y emite nuevos tokens criptográficos, que se pueden comparar con el almacenado, cada vez que se representa una página. Ver request_forgery_protection.rb .
Lecciones
Use authenticity_token
para proteger sus métodos no idempotentes (POST, PUT / PATCH y DELETE). También asegúrese de no permitir ninguna solicitud GET que pueda modificar recursos en el servidor.
EDITAR: Verifique el comentario de @erturne con respecto a las solicitudes GET que están siendo idempotent. Lo explica de una manera mejor que la que he hecho aquí.
Métodos donde se requiere authenticity_token
authenticity_token
es obligatorio en el caso de métodos idempotentes como publicar, poner y eliminar, ya que los métodos idempotentes afectan a los datos.
Por qué se requiere
Se requiere para prevenir de las malas acciones. authenticity_token se almacena en una sesión, siempre que se crea un formulario en las páginas web para crear o actualizar los recursos, un token de autenticidad se almacena en un campo oculto y se envía con el formulario en el servidor. Antes de ejecutar la acción, el usuario autenticó el token autenticado se verifica en forma cruzada con el token
authenticity_token
almacenado en la sesión. Siauthenticity_token
es el mismo, entonces el proceso continúa, de lo contrario no realiza acciones.