Rails: no se puede verificar la autenticidad del token CSRF al realizar una solicitud POST

89

Quiero hacerle POST requesta mi desarrollador local, así:

  HTTParty.post('http://localhost:3000/fetch_heroku',
                :body => {:type => 'product'},)

Sin embargo, desde la consola del servidor informa

Started POST "/fetch_heroku" for 127.0.0.1 at 2016-02-03 23:33:39 +0800
  ActiveRecord::SchemaMigration Load (0.0ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by AdminController#fetch_heroku as */*
  Parameters: {"type"=>"product"}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 1ms

Aquí está mi controlador y configuración de rutas, es bastante simple.

  def fetch_heroku
    if params[:type] == 'product'
      flash[:alert] = 'Fetch Product From Heroku'
      Heroku.get_product
    end
  end

  post 'fetch_heroku' => 'admin#fetch_heroku'

¿No estoy seguro de lo que debo hacer? Desactivar el CSRF ciertamente funcionaría, pero creo que debería ser mi error al crear una API de este tipo.

¿Hay alguna otra configuración que deba hacer?

cqcn1991
fuente
5
Para las API, generalmente se acepta desactivar la validación del token CSRF . Yo uso protect_from_forgery with: :null_session.
dcestari

Respuestas:

110

La falsificación de solicitudes entre sitios (CSRF / XSRF) se produce cuando una página web maliciosa engaña a los usuarios para que realicen una solicitud que no está destinada, por ejemplo, mediante el uso de marcadores, iframes o simplemente creando una página que sea visualmente lo suficientemente similar como para engañar a los usuarios.

La protección CSRF de Rails está hecha para aplicaciones web "clásicas"; simplemente brinda un grado de seguridad de que la solicitud se originó en su propia aplicación web. Un token CSRF funciona como un secreto que solo su servidor conoce: Rails genera un token aleatorio y lo almacena en la sesión. Sus formularios envían el token a través de una entrada oculta y Rails verifica que cualquier solicitud que no sea GET incluya un token que coincida con lo que está almacenado en la sesión.

Sin embargo, una API suele ser, por definición, entre sitios y está destinada a ser utilizada en más que su aplicación web, lo que significa que todo el concepto de CSRF no se aplica del todo.

En su lugar, debe utilizar una estrategia basada en token para autenticar las solicitudes de API con una clave y un secreto de API, ya que está verificando que la solicitud proviene de un cliente de API aprobado, no de su propia aplicación.

Puede desactivar CSRF como lo señala @dcestari:

class ApiController < ActionController::Base
  protect_from_forgery with: :null_session
end

Actualizado. En Rails 5 puede generar aplicaciones solo API usando la --apiopción:

rails new appname --api

No incluyen el middleware CSRF y muchos otros componentes superfluos.

max
fuente
Gracias, elijo desactivar parte del CSRF: stackoverflow.com/questions/5669322/…
cqcn1991
4
esto sugiere que todas las API brindan tráfico de aplicación a aplicación (en el que un solo socio recibe una clave y un secreto únicos). En ese escenario, como una comunicación de servidor a servidor, esta respuesta es apropiada. SIN EMBARGO, lo que confunde a la mayoría de los desarrolladores de aplicaciones web es que para un cliente Javascript, controlado y escrito por usted, NO DESEA usar una única clave secreta (que expondría una única clave secreta a todos los clientes). En cambio, el mecanismo de sesión de cookies y CSRF de Rail funciona muy bien, incluso para aplicaciones Javascript que usan su API, si pasa el token CSRF a Rails con cada solicitud.
Jason FB
la forma en que hace esto en AJAX se describe en esta publicación SO stackoverflow.com/questions/7203304/…
Jason FB
@JasonFB tiene razón en que un solo secreto con clientes javascript no funciona. Sin embargo, el uso de sesiones y la protección CSRF de Rails sigue siendo problemático si tiene la intención de crear una API que también pueda ser utilizada por otros tipos de clientes que no sean navegadores.
máximo
Si proporciona o crea una alternativa a CSRF, sea mi invitado. Solo conozca el motivo de lo que está reemplazando para saber con qué es apropiado reemplazarlo. Obviamente, ahora nuestra objeción se vuelve contextual.
Jason FB
77

Otra forma de desactivar CSRF que no generará una sesión nula es agregar:

skip_before_action :verify_authenticity_token

en su controlador Rails. Esto asegurará que aún tenga acceso a la información de la sesión.

Nuevamente, asegúrese de hacer esto solo en controladores API o en otros lugares donde la protección CSRF no se aplica del todo.

Matt Waldron
fuente
1
¿Esto es lo mismo que protect_from_forgery except: [:my_method_name]?
Arnold Roa
19

Hay información relevante sobre una configuración de CSRF con respecto a los controladores API en api.rubyonrails.org :

Es importante recordar que las solicitudes XML o JSON también se ven afectadas y si está creando una API , debe cambiar el método de protección contra falsificaciones en ApplicationController(por defecto :) :exception:

class ApplicationController < ActionController::Base
  protect_from_forgery unless: -> { request.format.json? }
end

Es posible que deseemos deshabilitar la protección CSRF para las API, ya que generalmente están diseñadas para no tener estado. Es decir, el cliente de la API de solicitud manejará la sesión por usted en lugar de Rails.

Sr. Tao
fuente
4
Esto es tan confuso. Estoy oscilando entre las fuentes que dicen que protect_from_forgerytodavía es necesario para las API y las fuentes que dicen que no lo es. ¿Qué pasa si la API es para una aplicación de una sola página, que usa la cookie de sesión para la autenticación del usuario?
Wylliam Judd
El mecanismo CSRF es la forma integrada de Rails de lidiar con el vector de ataque utilizando cookies de sesión. El mecanismo protege sus controladores, mientras opera junto (en realidad, dentro) de la cookie de sesión de Rails. Sin protect_from_forgery, o sin apagarlo o establecer una excepción, le está diciendo a Rails que NO proteja esa acción usando la información del token CSRF (que se toma de la cookie de sesión).
Jason FB
5
Creo que lo que es confuso es que para la documentación de Rails, "API" significa una aplicación de servidor a servidor que recibirá solicitudes de API de socios remotos de confianza (no todos los usuarios web que visitan su sitio). Para esas personas, proporcione pares clave-secreto únicos a través de un mecanismo seguro no pirateable. Para las aplicaciones web modernas, donde mucha gente cargará su aplicación web a través de un cliente web, aún utiliza una sesión o un mecanismo basado en token para identificar de forma única a cada persona que visita su sitio. Entonces, a menos que esté usando ALGÚN OTRO mecanismo para hacer esto (tokens Web Json, etc.), quédese con las cosas integradas de Rails.
Jason FB
1
la forma en que hace esto en AJAX se describe en esta publicación SO stackoverflow.com/questions/7203304/…
Jason FB
12

Desde Rails 5 también puede crear una nueva clase con :: API en lugar de :: Base:

class ApiController < ActionController::API
end
webaholik
fuente
3

Si desea excluir la acción de muestra del controlador de muestra

class TestController < ApplicationController
  protect_from_forgery :except => [:sample]

  def sample
     render json: @hogehoge
  end
end

Puede procesar solicitudes desde el exterior sin ningún problema.

Ryosuke Hujisawa
fuente
0

La solución más simple para el problema es hacer cosas estándar en su controlador o puede ponerlo directamente en ApplicationController

class ApplicationController < ActionController::Base protect_from_forgery with: :exception, prepend: true end

Arish Khan
fuente