Comprender el token de autenticidad de Rails


983

Me encuentro con algunos problemas relacionados con el token de autenticidad en Rails, como lo he hecho muchas veces.

Pero realmente no quiero resolver este problema y continuar. Realmente me gustaría entender el token Autenticidad. Bueno, mi pregunta es, ¿tiene alguna fuente completa de información sobre este tema o pasaría su tiempo explicando los detalles aquí?


77
Ver también: "¿Por qué Google antecede (1) a su respuesta JSON?" stackoverflow.com/questions/2669690/…
Chloe

Respuestas:


1463

Lo que pasa

Cuando el usuario ve un formulario para crear, actualizar o destruir un recurso, la aplicación Rails crea un formulario aleatorio authenticity_token, 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 lo busca authenticity_token, lo compara con el almacenado en la sesión y, si coinciden, se permite que la solicitud continúe.

Por que sucede

Como el token de autenticidad se almacena en la sesión, el cliente no puede conocer su valor. Esto evita que las personas envíen formularios a una aplicación 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 utilizar el servicio B, vio una imagen que le gusta y presionó la imagen para ver un tamaño mayor. Ahora, si había algún código maligno en el servicio B, podría enviar una solicitud al servicio A (en el que ha iniciado sesión) y solicitar que elimine su cuenta enviando una solicitud a http://serviceA.com/close_account. Esto es lo que se conoce como CSRF (Cross Site Request Forgery) .

Si el servicio A está usando 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 permitirá que continúe.

Los documentos de la API describen detalles sobre la metaetiqueta:

La protección CSRF se activa con el protect_from_forgerymétodo, que verifica el token y restablece la sesión si no coincide con lo esperado. Se genera una llamada a este método para las nuevas aplicaciones de Rails de forma predeterminada. El parámetro del token se nombra authenticity_tokende manera predeterminada. El nombre y el valor de este token se deben agregar a cada diseño que muestre formularios al incluirlos csrf_meta_tagsen 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 verifica para el token de autenticidad. ¿Por qué? porque la especificación HTTP establece que las solicitudes GET son idempotentes y no deberían crear, alterar o destruir recursos en el servidor, y la solicitud debería ser idempotente (si ejecuta el mismo comando varias veces, debería obtener el mismo resultado cada vez).

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 procesa una página. Ver request_forgery_protection.rb .

Lecciones

Úselo authenticity_tokenpara proteger sus métodos no idempotentes (POST, PUT / PATCH y DELETE). También asegúrese de no permitir ninguna solicitud GET que pueda modificar los recursos en el servidor.


EDITAR: Verifique el comentario de @erturne sobre las solicitudes GET como idempotentes. Él lo explica de una mejor manera que lo que he hecho aquí.


25
@Faisal, ¿es posible entonces que un atacante simplemente lea / capture el elemento 'oculto' del formulario para el Servicio A y obtenga ese token único generado para el usuario, dado que ha obtenido acceso a la sesión iniciada por el usuario? para el servicio A?
marcamillion

11
@marcamillion: si alguien secuestró tu sesión en el servicio A, entonces el token de autenticidad no te protegerá. El secuestrador podrá enviar una solicitud y se le permitirá continuar.
Faisal

12
@zabba: Rails genera una excepción ActionController :: InvalidAuthenticityToken si se envía un formulario sin el token adecuado. Puede rescatar_de la excepción y hacer el procesamiento que desee.
Faisal

55
re "Asegúrese también de no realizar ninguna solicitud GET que pueda modificar los recursos en el servidor". - esto incluye no usar match () en rutas que podrían permitir solicitudes GET a acciones del controlador destinadas a recibir solo POSTs
Steven Soroka

102
"... y la solicitud debe ser idempotente (si ejecuta el mismo comando varias veces, debería obtener el mismo resultado cada vez)". Solo una sutil aclaración aquí. Seguro significa que no hay efectos secundarios. Idempotente significa el mismo efecto secundario sin importar cuántas veces se llame a un servicio. Todos los servicios seguros son inherentemente idempotentes porque no hay efectos secundarios. Llamar a GET en un recurso de tiempo actual varias veces devolvería un resultado diferente cada vez, pero es seguro (y por lo tanto idempotente).
Erturne

137

El token de autenticidad está diseñado para que sepa que su formulario se envía desde su sitio web. Se genera a partir de 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 está teniendo dificultades con los rieles que le niegan el acceso al script AJAX, puede usar

<%= form_authenticity_token %>

para generar el token correcto cuando está creando su formulario.

Puede leer más al respecto en la documentación .


88

¿Qué es CSRF?

El token de autenticidad es una contramedida para la falsificación de solicitudes entre sitios (CSRF). ¿Qué es CSRF, preguntas?

Es una forma en que un atacante puede potencialmente secuestrar sesiones sin siquiera conocer los 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 previamente completados 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.
  • El formulario puede estar en un iframe que es invisible, por lo que nunca se sabe que ocurrió el ataque.
  • Esto se llama falsificación de solicitudes entre sitios (CSRF).

Solución CSRF :

  • El servidor puede marcar formularios que provienen 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).
  • El servidor proporciona un token válido en formularios en sus páginas.
  • El servidor verifica el token cuando se publica el formulario, rechaza los formularios sin el token adecuado.
  • Token de ejemplo: identificador de sesión cifrado con clave secreta del servidor.
  • Rails genera automáticamente tales tokens: vea el campo de entrada authenticity_token en cada formulario.

1
Aquí hay una versión de esta misma explicación que es menos precisa pero también menos abstracta: stackoverflow.com/a/33829607/2810305
Lutz Prechelt el

No estoy seguro, pero, ¿permiten los navegadores modernos enviar solicitudes no idempotentes (POST / PUT / DELETE) a otro dominio? Supongo que debe haber protección contra tales cosas en el navegador mismo
DivideByZero

45

Ejemplo de ataque mínimo que se evitaría: CSRF

En mi sitio web evil.comlo convenzo de enviar 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, las cookies se enviarán y la transferencia se realizará sin que usted lo sepa.

Ahí es donde entra en juego el token CSRF:

  • 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

Entonces 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 authenticity_tokenparámetro, y no hay forma de que lo haya adivinado ya que es un gran número aleatorio.

Esta técnica de prevención se llama Patrón de token de sincronizador .

Política del mismo origen

Pero, ¿qué sucede si el atacante realizó dos solicitudes con JavaScript, una para leer el token y la segunda para realizar la transferencia?

¡El patrón de token sincronizador solo no es suficiente para evitar eso!

Aquí es donde la Política del mismo origen viene al rescate, como lo he explicado en: /security/8264/why-is-the-same-origin-policy-so-important/72569# 72569

Cómo envía los tokens Rails

Cubierto en: Rails: ¿Cómo funciona csrf_meta_tag?

Básicamente:

  • Los ayudantes HTML como form_tagagregar un campo oculto al formulario para usted si no es un formulario GET

  • Ajax se ocupa automáticamente de jquery-ujs , que lee el token de los metaelementos agregados a su encabezado por csrf_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é obsoletos.

Otros enfoques de prevención


Gracias, pero su punto sobre confiar en la misma política de origen para no poder leer el token CSRF primero parece defectuoso. Entonces, primero dice que puede PUBLICAR en un origen diferente pero no puede leer, parece extraño, pero supongo que es correcto, pero podría inyectar una etiqueta de imagen o script con un acceso a la página y vincular un controlador para analizar la respuesta y entiendes si?
bjm88

@ bjm88 inyectar el script donde? ¿En su sitio o en el sitio atacado? Si se ataca el sitio, permitir la inyección de scripts es una falla de seguridad bien conocida, y efectivamente empeña el sitio web. Cada sitio web debe combatirlo mediante el saneamiento de entrada. Para las imágenes, no veo cómo se pueden usar para un ataque. En el sitio de ataque: puede modificar su navegador para permitir la lectura y, por lo tanto, auto empeñarse a voluntad :-) pero los navegadores decentes lo impiden por defecto, inténtelo.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

43

El token de autenticidad se usa para evitar 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:

ingrese la descripción de la imagen aquí

Un pirata informático podría enviar una solicitud HTTP al servidor diciendo GET /transfer?amount=$1000000&account-to=999999, ¿verdad?

ingrese la descripción de la imagen aquí

Incorrecto. El ataque de los hackers no funcionará. El servidor básicamente pensará?

¿Eh? ¿Quién es este tipo tratando de iniciar una transferencia? No es el propietario de la cuenta, eso es seguro.

¿Cómo sabe esto el servidor? Porque no hay session_idcookies que autentiquen al solicitante.

Cuando inicia sesión con su nombre de usuario y contraseña, el servidor establece una session_idcookie 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 session_idcookie, el servidor sabe:

Oh, ese es John Doe. Ingresó exitosamente hace 2.5 minutos. Él es bueno para irse.

Un hacker podría pensar:

Hmm Una solicitud HTTP normal no funcionará, pero si pudiera conseguir esa session_idcookie, sería oro.

El navegador de los usuarios tiene un conjunto de cookies establecidas para el bank.comdominio. Cada vez que el usuario realiza una solicitud al bank.comdominio, se envían todas las cookies. Incluyendo elsession_id galleta.

Así que si un pirata informático podría conseguir que haga la solicitud GET que transfiere dinero a su cuenta, tendría éxito. ¿Cómo podría engañarte para que lo hagas? Con falsificación de solicitud de sitio cruzado.

Es bastante simple, en realidad. El hacker podría hacerte visitar 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 del usuario encuentre esa etiqueta de imagen, realizará una solicitud GET a esa URL. Y dado que la solicitud proviene de su navegador, enviará con ella todas las cookies asociadas bank.com. Si el usuario había iniciado sesión recientemente en bank.com... la session_idcookie se establecerá, ¡y el servidor pensará que el usuario tenía la intención de transferir $ 1,000,000 a la cuenta 999999!

ingrese la descripción de la imagen aquí

Bueno, simplemente no visites sitios peligrosos y estarás bien.

Eso no es suficiente. ¿Qué pasa si alguien publica esa imagen en Facebook y aparece en su muro? ¿Qué pasa si se inyecta en un sitio que estás visitando con un ataque XSS?

No es tan malo. Solo 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 tienes ApplicationControlleresto:

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="&#x2713;" />
  <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 un valor diferente al azar cada vez que se genera el formulario:

ingrese la descripción de la imagen aquí

Un ataque Cross Site Scripting (XSS): así es como. Pero esa es una vulnerabilidad diferente para un día diferente.



34

ya que Authenticity Tokenes muy importante, y en Rails 3.0+ puedes usar

 <%= token_tag nil %>

crear

<input name="authenticity_token" type="hidden" value="token_value">

en cualquier sitio


Esto fue útil para mí. Realmente estaba tratando de hacerlo XSSen la página de inicio de sesión, no con fines nefastos, sino para crear una nueva sesión con un nombre de usuario precargado. Ahora sé que solo puedo usar value="token_value".
Michael - ¿Dónde está Clay Shirky?

27

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/


11

Métodos donde authenticity_tokense requiere

authenticity_token se requiere en el caso de métodos idempotentes como publicar, poner y eliminar, porque los métodos idempotentes están afectando a los datos.

Por qué es obligatorio

Se requiere para evitar acciones malvadas. authenticity_token se almacena en sesión, cada vez que se crea un formulario en páginas web para crear o actualizar recursos, se almacena un token de autenticidad en un campo oculto y se envía con el formulario en el servidor. Antes de ejecutar la acción, el usuario enviado autenticidad_token se verifica de forma cruzada con authenticity_tokenalmacenada en la sesión. Si authenticity_tokenes el mismo, el proceso continúa; de lo contrario, no realiza acciones.


3
En realidad, ¿no es todo lo contrario? GET es idempotente ya que su llamada no debería alterar el estado del sistema, donde los verbos PUT POST y DELETE NO son verbos idempotentes ya que alteran el estado del sistema. IE: authenticity_token se requiere en el caso de métodos NO idempotentes.
Jean-Théo

2
@ Jean-Daube, uma: idempotente significa que si se hace dos veces, la acción solo ocurre una vez. GET, PUT y DELETE son idempotentes: w3.org/Protocols/rfc2616/rfc2616-sec9.html La propiedad clave aquí no es idempotencia, pero si el método cambia o no los datos, que se llama "Método seguro" o no.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

6

¿Qué es una autenticación_token?

Esta es una cadena aleatoria utilizada por la aplicación rails para asegurarse de que el usuario solicita o realiza 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_token?

Para proteger su aplicación o sitio de la falsificación de solicitudes entre sitios.

¿Cómo agregar un token de autenticación a un formulario?

Si está generando un formulario usando form_for tag, se agrega automáticamente un token de autenticación, de lo contrario puede usarlo <%= csrf_meta_tag %>.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.