rieles - "ADVERTENCIA: No se puede verificar la autenticidad del token CSRF" para las solicitudes de dispositivos json

82

¿Cómo puedo recuperar el token CSRF para pasar con una solicitud JSON?

Sé que, por razones de seguridad, Rails está verificando el token CSRF en todos los tipos de solicitud (incluido JSON / XML).

Podría poner mi controlador skip_before_filter :verify_authenticity_token, pero perdería la protección CRSF (no es recomendable :-)).

Esta respuesta similar (aún no aceptada) sugiere

Recupera el token con <%= form_authenticity_token %>

La pregunta es ¿cómo? ¿Necesito hacer una primera llamada a cualquiera de mis páginas para recuperar el token y luego hacer mi autenticación real con Devise? ¿O es una información única que puedo obtener de mi servidor y luego usar de manera consistente (hasta que la cambie manualmente en el servidor)?

Comunidad
fuente

Respuestas:

127

EDITAR :

En Rails 4 ahora uso lo que sugiere @genkilabs en el siguiente comentario:

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }

Lo cual, en lugar de desactivar por completo la seguridad incorporada, elimina cualquier sesión que pueda existir cuando algo llega al servidor sin el token CSRF.


skip_before_filter :verify_authenticity_token, :if => Proc.new { |c| c.request.format == 'application/json' }

Esto desactivaría la verificación CSRF para las publicaciones / put de json que se hayan marcado correctamente como tales.

Por ejemplo, en iOS, establezca lo siguiente en su NSURLRequest donde "parámetros" son sus parámetros:


[request setHTTPMethod:@"POST"];

[request setValue:@"application/json" 
       forHTTPHeaderField:@"content-type"];

[request setValue:@"application/json" 
       forHTTPHeaderField:@"accept"];

[request setHTTPBody:[NSData dataWithBytes:[parameters UTF8String] 
                                            length:[parameters length]]];
Ryan Crews
fuente
3
Al hacer esto, está expuesto a un ataque si el atacante está marcando el formato como 'json'. Y es por eso que Rails, por definición, está emitiendo una advertencia. Quiero que mi json pueda pasar correctamente un token; de lo contrario, tener la advertencia en los registros está bien.
21
está expuesto a ataques, por lo que recomiendan que tenga alguna otra seguridad en su lugar cuando apague el filtro. Normalmente, para las solicitudes de API, el solicitante enviaría una clave de API junto con los datos de publicación, que luego verificaría antes de ejecutar el método deseado.
Ryan Crews
18
En Rails 4 puedes hacer algo como:protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/json' }
genkilabs
2
@genkilabs Me gusta eso, agregado para responder a los usuarios de rieles 4, gracias =]
Ryan Crews
1
Dos cosas que hice hace un momento para que esto funcione en mi aplicación Rails & iOS: 1) No vi ninguna razón por la que estábamos mezclando sintaxis hash de estilo antiguo y nuevo en Ruby, por lo que el código en mi controlador se ve así: protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }2) No relacionado con esta pregunta pero relacionado con el tema de PUBLICAR JSON desde iOS, ya que estoy usando AFNetworkingy Rails no ajustaba los parámetros automáticamente, hice lo siguiente:manager.requestSerializer = [AFJSONRequestSerializer serializer];
mharper
18

Puede enviar el token CSRF, después de iniciar sesión correctamente, utilizando un encabezado personalizado.

Por ejemplo, ponga esto en sus sesiones # create:

response.headers['X-CSRF-Token'] = form_authenticity_token

Ejemplo de encabezado de respuesta de inicio de sesión que proporciona el token CSRF:

HTTP/1.1 200 OK
Cache-Control: max-age=0, private, must-revalidate
Connection: Keep-Alive
Content-Length: 35
Content-Type: application/json; charset=utf-8
Date: Mon, 22 Oct 2012 11:39:04 GMT
Etag: "9d719d3b9aabd413c3603e04e8a3933d"
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-10-12)
Set-Cookie: [cut for readability] 
X-Csrf-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=
X-Request-Id: 178746992d7aca928c876818fcdd4c96
X-Runtime: 0.169792
X-Ua-Compatible: IE=Edge

Este token es válido hasta que vuelva a iniciar sesión o (cierre la sesión si lo admite a través de su API). Su cliente puede extraer y almacenar el token de los encabezados de respuesta de inicio de sesión. Luego, cada solicitud POST / PUT / DELETE debe configurar el encabezado X-CSRF-Token con el valor recibido en el momento del inicio de sesión.

Ejemplos de encabezados POST con el token CSRF:

POST /api/report HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate, compress
Content-Type: application/json; charset=utf-8
Cookie: [cut for readability]
Host: localhost:3000
User-Agent: HTTPie/0.3.0
X-CSRF-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=

Documentación: form_authenticity_token

tasos
fuente
18

De hecho, la forma más sencilla. No se moleste en cambiar los encabezados.

Asegúrate de tener:

<%= csrf_meta_tag %>

en tus layouts/application.html.erb

Simplemente haga un campo de entrada oculto como este:

<input name="authenticity_token" 
       type="hidden" 
       value="<%= form_authenticity_token %>"/>

O si quieres una publicación de jquery ajax:

$.ajax({     
    type: 'POST',
    url: "<%= someregistration_path %>",
    data: { "firstname": "text_data_1", "last_name": "text_data2", "authenticity_token": "<%= form_authenticity_token %>" },                                                                                  
    error: function( xhr ){ 
      alert("ERROR ON SUBMIT");
    },
    success: function( data ){ 
      //data response can contain what we want here...
      console.log("SUCCESS, data="+data);
    }
});

Básicamente, cuando publique sus datos json, simplemente agregue un campo Authenticity_token válido a los postdatos y la advertencia debería desaparecer ...

Walter Schreppers
fuente
En mi humilde opinión, no solo debería poner una advertencia en el registrador, sino descartar por completo las publicaciones sin una etiqueta csrf válida de forma predeterminada, solo publica la advertencia y envía los datos muy bien ...
Walter Schreppers
La forma más corta de hacer el campo de entrada del token de autenticidad oculta es = token_tag(nil)(nada es importante)
Yo Ludke
En versiones anteriores de rieles, la etiqueta termina en plural. <% = csrf_meta_tags%>
cyonder
4

Resolví ese error de esta manera:

class ApplicationController < ActionController::Base
  protect_from_forgery
  skip_before_action :verify_authenticity_token, if: :json_request?

  protected

  def json_request?
    request.format.json?
  end
end

Fuente: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html

gusjabar
fuente
No estoy seguro de que desee hacer esto: los navegadores pueden usar API JSON.
Joe Van Dyk
¡Gracias! Necesitaba una forma rápida (y sucia) de resolver este problema y esto podría funcionar lo suficientemente bien para lo que estoy tratando de lograr.
NerdyTherapist
El documento vinculado (ahora actualizado para Rails 5) parece ser: protect_from_forgery with: :exception, unless: -> { request.format.json? }y funcionó para mí. Gracias.
Akostadinov
1
Esto simplemente desactiva la verificación, lo que evita el problema. Si una aplicación va a desactivar esto, ¿cuál es el punto de tener activada la protección contra falsificaciones?
jefflunt
3

Lo preocupante es que en Rails 3.2.3 ahora recibimos la advertencia CSRF en production.log, ¡pero la publicación no falla! Quiero que falle ya que me protege de los ataques. Y puede agregar el token csrf con jquery antes del filtro por cierto:

http://jasoncodes.com/posts/rails-csrf-vulnerability

Walter Schreppers
fuente
2

He utilizado el siguiente. ¿Utilizando incluir? así que si el tipo de contenido es application / json; charset = utf-8 entonces todavía está funcionando.

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format.include? 'application/json' }
DaCart
fuente
2

Esta respuesta es mejor.

Puede mantener la validación CSRF-TOKEN sin esfuerzo adicional (el token se adjunta) antes de cualquier envío XMLHttpRequest. Sin JQuery, sin nada, solo copiar / pegar y actualizar.

Simplemente agregue este código.

(function() {
    var send = XMLHttpRequest.prototype.send,
        token = $('meta[name=csrf-token]').attr('content');
    XMLHttpRequest.prototype.send = function(data) {
        this.setRequestHeader('X-CSRF-Token', token);
        return send.apply(this, arguments);
    };
}());
Emanuel
fuente
1

Tuve el mismo problema con la siguiente versión de Rails:
gem 'rails',: git => 'git: //github.com/rails/rails.git',: branch => '3-2-stable'

Actualicé a 3.2.2 y ahora todo funciona bien para mí. :)
gem 'rieles', '3.2.2'

Olivier Grimard
fuente
Gracias por la sugerencia. Lo probé, pero todavía tengo la misma advertenciaWARNING: Can't verify CSRF token authenticity
0

Me encontré con el mismo problema esta noche. La razón por la que sucede es porque cuando inicia sesión, el último token csrf ya no es válido. Lo que hice fue: $("meta[name=csrf-token]").attr('content', '<%= form_authenticity_token %>');en su aplicación / views / devise / sessions / create.js.rb.

Ahora tiene un token csrf válido :) Espero que ayude

nbit001
fuente
0

También para el modo de desarrollo / prueba.

protect_from_forgery with: :exception unless %w(development test).include? Rails.env

Esta advertencia se muestra porque está utilizando :null_session, en Rails 4.1 funciona de forma predeterminada si no se with:especifican opciones.

protect_from_forgery
merqlove
fuente