Rails 4 Token de autenticidad

198

Estaba trabajando en una nueva aplicación Rails 4 (en Ruby 2.0.0-p0) cuando me encontré con algunos problemas de token de autenticidad.

Mientras escribía un controlador que responde a json (usando el respond_tométodo de clase), llegué a la createacción y comencé a obtener ActionController::InvalidAuthenticityTokenexcepciones cuando intenté crear un registro usando curl.

Me aseguré de configurar -H "Content-Type: application/json"y configurar los datos con, -d "<my data here>"pero todavía no tuve suerte.

Intenté escribir el mismo controlador usando Rails 3.2 (en Ruby 1.9.3) y no tuve ningún problema con el token de autenticidad. Busqué y vi que había algunos cambios con los tokens de autenticidad en Rails 4. Por lo que entiendo, ¿ya no se insertan automáticamente en los formularios? Supongo que esto está afectando de alguna manera los tipos de contenido no HTML.

¿Hay alguna forma de evitar esto sin tener que solicitar un formulario HTML, arrebatando el token de autenticidad y luego haciendo otra solicitud con ese token? ¿O me estoy perdiendo por completo algo que es completamente obvio?

Editar: acabo de intentar crear un nuevo registro en una nueva aplicación Rails 4 usando un andamio sin cambiar nada y me encuentro con el mismo problema, así que supongo que no es algo que hice.

alexcoco
fuente

Respuestas:

277

Creo que acabo de descubrirlo. Cambié el (nuevo) predeterminado

protect_from_forgery with: :exception

a

protect_from_forgery with: :null_session

según el comentario en ApplicationController.

# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.

Puede ver la diferencia mirando la fuente request_forgery_protecton.rbo, más específicamente, las siguientes líneas:

En Rails 3.2 :

# This is the method that defines the application behavior when a request is found to be unverified.
# By default, \Rails resets the session when it finds an unverified request.
def handle_unverified_request
  reset_session
end

En Rails 4 :

def handle_unverified_request
  forgery_protection_strategy.new(self).handle_unverified_request
end

Lo cual llamará lo siguiente :

def handle_unverified_request
  raise ActionController::InvalidAuthenticityToken
end
alexcoco
fuente
20
Puede eliminar: with option all together,: null_session es el valor predeterminado: api.rubyonrails.org/classes/ActionController/…
Casey
66
¿Hay alguna forma de usar una excepción para llamadas que no sean JSON y una sesión nula para llamadas JSON (también llamadas de API)?
James McMahon el
@JamesMcMahon Es posible que pueda escribir uno propio before_actionque verifique el formato y si la solicitud está verificada. No conozco ninguna forma integrada de establecer condiciones.
alexcoco
1
En los carriles 4.1.6, tuve que especificar también skip_before_action :verify_authenticity_tokenen el controlador de la aplicación de mi API para que esto funcione.
Waseem
1
No debería tener que deshabilitar :verify_authenticy_tokenmás en Rails 4.2. Su valor predeterminado es :null_session, que, como su nombre lo indica, solo le da una solicitud sin una sesión. En su lugar, puede verificar la solicitud a través de una clave API.
lobati
72

En lugar de desactivar la protección csrf, es mejor agregar la siguiente línea de código en el formulario

<%= tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) %> 

y si está usando form_for o form_tag para generar el formulario, entonces agregará automáticamente la línea de código anterior en el formulario

xiaoboa
fuente
9
Este es un buen consejo si está haciendo un formulario, pero la pregunta se refiere a hacer llamadas API utilizando JSON.
James McMahon el
1
Sin embargo, gracias, ¡esto respondió una pregunta que tenía sobre escribir un código a mano mientras usaba el manillar!
David Routen
70

Agregar la siguiente línea en el formulario funcionó para mí:

<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
Carlos
fuente
12
en realidad no se aplicará a las llamadas de la API
courtsimas
2
En mi caso, estoy usando Rails4. Puedo enviar el formulario haciendo clic en el botón Enviar. Pero si envío el formulario a través del código JS, se produce este error. Y esta respuesta solucionó el problema para mí.
Chris.Zou
2
Para mi aplicación basada en Rails 4.0.8 fue suficiente para escribir =token_tag nilo (en .erb)<%= token_tag nil %>
jmarceli
1
¿Parece que está deshabilitando la autenticación al establecer el token en nulo?
Carlos
¿Es seguro hacer esto?
Angel Garcia
33

No creo que sea bueno desactivar generalmente la protección CSRF siempre que no implementes exclusivamente una API.

Al mirar la documentación de la API de Rails 4 para ActionController , descubrí que puede desactivar la protección contra falsificaciones en una base por controlador o por método.

Por ejemplo, para desactivar la protección CSRF para los métodos que puede usar

class FooController < ApplicationController
  protect_from_forgery except: :index
itinerancia
fuente
Esto lo hizo por mí en Rails 4: se produjo un error después de intentar eliminar. ^^
zero_cool
9

Encontré el mismo problema. Lo arreglé agregando a mi controlador:

      skip_before_filter :verify_authenticity_token, if: :json_request?
usuario1756254
fuente
método indefinido `json_request? ' para # <SettingsController: 0x000000030a54a8>, ¿Alguna idea?
Deano
Debe definir un método como: def json_request? request.format.json? final
Jai Kumar Rajput
7

¿Has probado?

 protect_from_forgery with: :null_session, if: Proc.new {|c| c.request.format.json? }
zechtz
fuente
2

Estas características se agregaron por motivos de seguridad y protección contra falsificaciones.
Sin embargo, para responder a su pregunta, aquí hay algunas entradas. Puede agregar estas líneas después del nombre del controlador.

Al igual que,

class NameController < ApplicationController
    skip_before_action :verify_authenticity_token

Aquí hay algunas líneas para diferentes versiones de rieles.

Carriles 3

skip_before_filter: generate_authenticity_token

Carriles 4 :

skip_before_action: generate_authenticity_token


Si tiene la intención de deshabilitar esta característica de seguridad para todas las rutinas del controlador, puede cambiar el valor de protect_from_forgery a : null_session en su archivo application_controller.rb.

Al igual que,

class ApplicationController < ActionController::Base
  protect_from_forgery with: :null_session
end
Kent Aguilar
fuente
1

Si está utilizando jQuery con rieles, tenga cuidado de permitir la entrada a métodos sin verificar el token de autenticidad.

jquery-ujs puede administrar los tokens por ti

Ya debería tenerlo como parte de la gema jquery-rails, pero es posible que deba incluirlo en application.js con

//= require jquery_ujs

Eso es todo lo que necesita: su llamada ajax ahora debería funcionar

Para obtener más información, consulte: https://github.com/rails/jquery-ujs

usuario1555400
fuente
1

Agregar authenticity_token: truea la etiqueta del formulario

Salma Gomaa
fuente
0

Cuando define su propio formulario html, debe incluir una cadena de token de autenticación, que debe enviarse al controlador por razones de seguridad. Si usa rails form helper para generar la autenticidad, el token se agrega al formulario de la siguiente manera.

<form accept-charset="UTF-8" action="/login/signin" method="post">
  <div style="display:none">
    <input name="utf8" type="hidden" value="&#x2713;" />
    <input name="authenticity_token" type="hidden" value="x37DrAAwyIIb7s+w2+AdoCR8cAJIpQhIetKRrPgG5VA=">
  </div>
    ...
</form>

Entonces, la solución al problema es agregar el campo authenticity_token o usar rails de ayudantes en lugar de comprometer la seguridad, etc.

amjad
fuente
1
Gracias por su respuesta, aunque esto no responde a la pregunta original. La pregunta se refería a responder a las solicitudes JSON, pero está proporcionando una solución para un formulario HTML desde cero. Al realizar solicitudes JSON (piense en la API), no está enviando un formulario HTML y es posible que no tenga fácil acceso al token de autenticidad.
alexcoco
a menos que el contenido de la solicitud es en realidad JSON en cuyo caso se desea establecer a application/json.
alexcoco
Entiendo que la respuesta no es exactamente lo que estás buscando. Pero el propósito de dar una respuesta relacionada es ayudar a los usuarios que buscan "Problemas de autenticidad" similares.
amjad
lo siento, lo eliminé por error: "puede configurar Content-Type: application / x-www-form-urlencoded con la solución anterior".
amjad
0

Todas mis pruebas estaban funcionando bien. Pero por alguna razón, configuré mi variable de entorno como no probada:

export RAILS_ENV=something_non_test

Olvidé desarmar esta variable por lo que comencé a obtener una ActionController::InvalidAuthenticityTokenexcepción.

Después de desestabilizar $RAILS_ENV, mis pruebas comenzaron a funcionar nuevamente.

mridula
fuente