ADVERTENCIA: no se pueden verificar los carriles de autenticidad de token CSRF

238

Estoy enviando datos desde la vista al controlador con AJAX y recibí este error:

ADVERTENCIA: no se puede verificar la autenticidad del token CSRF

Creo que tengo que enviar este token con datos.

¿Alguien sabe cómo puedo hacer esto?

Editar: mi solución

Hice esto poniendo el siguiente código dentro de la publicación de AJAX:

headers: {
  'X-Transaction': 'POST Example',
  'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
kbaccouche
fuente
77
¿tiene <% = csrf_meta_tag%> en su encabezado de diseño?
Anatoly
sí, así: <% = csrf_meta_tags%>
kbaccouche
66
¿tiene bibliotecas jquery-rails que proporcionan funcionalidad ajax del lado del cliente?
Anatoly
2
Y la forma HAML es agregar "= csrf_meta_tags"
Ege Akpinar
buena pregunta, gracias por preguntar
AMIC MING

Respuestas:

374

Usted debe hacer esto:

  1. Asegúrese de tener <%= csrf_meta_tag %>en su diseño

  2. Agregue beforeSenda toda la solicitud ajax para establecer el encabezado como se muestra a continuación:


$.ajax({ url: 'YOUR URL HERE',
  type: 'POST',
  beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
  data: 'someData=' + someData,
  success: function(response) {
    $('#someDiv').html(response);
  }
});

Para enviar token en todas las solicitudes que puede usar:

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
  }
});
Chau Hong Linh
fuente
55
¡Gracias! ¡Trabajó para mí como un encanto!
Misha Moroshko
35
La biblioteca jQuery UJS proporcionada por el equipo de Rails agrega el token CSRF a la solicitud jQuery AJAX automáticamente. El archivo README contiene instrucciones sobre cómo configurarlo. github.com/rails/jquery-ujs/blob/master/src/rails.js#L91
James Conroy-Finn
1
Tenga en cuenta que puede configurar el encabezado para todas las solicitudes a la vez con la función $ .ajaxSetup.
Pieter Jongsma
1
¡Excelente! He estado buscando por un tiempo esta respuesta. Funciona a la perfección. ¡Gracias!
cassi.lup
44
Como nota, si está utilizando jQuery UJS como se sugirió anteriormente, debe asegurarse de que rails-ujs include venga después de jquery include o fallará con el mismo error que el op.
Topher Fangio
31

La mejor manera de hacerlo es usarlo <%= form_authenticity_token.to_s %>para imprimir el token directamente en su código de rieles. No necesita usar javascript para buscar el dom para el token csrf como mencionan otras publicaciones. simplemente agregue la opción de encabezados como se muestra a continuación;

$.ajax({
  type: 'post',
  data: $(this).sortable('serialize'),
  headers: {
    'X-CSRF-Token': '<%= form_authenticity_token.to_s %>'
  },
  complete: function(request){},
  url: "<%= sort_widget_images_path(@widget) %>"
})
ADÁN
fuente
77
En lugar de hacer esto para cada comando ajax, puede agregar encabezados a $ .ajaxSetup () .
Scott McMillin
1
Prefiero recomendar usar esta respuesta ...
opsidao
14
Realmente no me gusta el enfoque de usar ERB en JavaScript.
radixhound
Esto te obliga a generar tu javascript con ERB, lo cual es muy limitante. Incluso si hay lugares donde ERB podría ser una buena opción, hay otros donde no lo es, y agregarlo solo para obtener el token sería un desperdicio.
sockmonk
22

Si no recuerdo mal, debe agregar el siguiente código a su formulario para deshacerse de este problema:

<%= token_tag(nil) %>

No olvides el parámetro.

auralbee
fuente
8
En realidad, esto debería ser: <%= token_tag(nil) %>. Luego obtienes el token generado automáticamente.
szeryf
15

De hecho, la forma más simple. No te molestes en cambiar los encabezados.

Asegúrate de tener:

<%= csrf_meta_tag %> in your layouts/application.html.erb

Simplemente haga un campo de entrada oculto así:

<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);
    }
});
Walter Schreppers
fuente
Agregar el campo de entrada oculto a mi formulario de entrada resolvió el problema para mí.
Teemu Leisti
Esto se hace automáticamente si utiliza los ayudantes de formulario de Rails.
Jason FB
13

La actualización de una aplicación anterior a Rails 3.1, incluida la metaetiqueta csrf, todavía no la resuelve. En el blog de rubyonrails.org, dan algunos consejos de actualización, y específicamente esta línea de jquery que debe ir en la sección principal de su diseño:

$(document).ajaxSend(function(e, xhr, options) {
 var token = $("meta[name='csrf-token']").attr("content");
  xhr.setRequestHeader("X-CSRF-Token", token);
});

tomado de esta publicación de blog: http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails .

En mi caso, la sesión se reiniciaba con cada solicitud de ajax. Agregar el código anterior resolvió ese problema.

Danny
fuente
10
  1. Asegúrese de tener <%= csrf_meta_tag %>en su diseño
  2. Agregue a beforeSendpara incluir el token csrf en la solicitud ajax para establecer el encabezado. Esto solo es necesario para las postsolicitudes.

El código para leer el csrf-token está disponible en rails/jquery-ujs, por lo tanto, es más fácil usarlo de la siguiente manera:

$.ajax({
  url: url,
  method: 'post',
  beforeSend: $.rails.CSRFProtection,
  data: {
    // ...
  }
})
nathanvda
fuente
Simple sin volver a implementar lo que ya incluye Rails. Esta debería ser la respuesta seleccionada.
jiehanzheng 01 de
En Rails 5.1 también funciona:headers: { 'X-CSRF-Token': Rails.csrfToken() }
olhor
@nathanvda, tal vez puedas responder esta pregunta similar: stackoverflow.com/questions/50159847/…
6

Las respuestas más votadas aquí son correctas, pero no funcionarán si realiza solicitudes entre dominios porque la sesión no estará disponible a menos que explícitamente le diga a jQuery que pase la cookie de la sesión. Aquí se explica cómo hacerlo:

$.ajax({ 
  url: url,
  type: 'POST',
  beforeSend: function(xhr) {
    xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  },
  xhrFields: {
    withCredentials: true
  }
});
grito de terror
fuente
¿Puedo preguntarle si puede responder a esta pregunta muy similar? stackoverflow.com/questions/50159847/…
5

Puedes escribirlo globalmente como a continuación.

JS normal:

$(function(){

    $('#loader').hide()
    $(document).ajaxStart(function() {
        $('#loader').show();
    })
    $(document).ajaxError(function() {
        alert("Something went wrong...")
        $('#loader').hide();
    })
    $(document).ajaxStop(function() {
        $('#loader').hide();
    });
    $.ajaxSetup({
        beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))}
    });
});

Guión de café:

  $('#loader').hide()
  $(document).ajaxStart ->
    $('#loader').show()

  $(document).ajaxError ->
    alert("Something went wrong...")
    $('#loader').hide()

  $(document).ajaxStop ->
    $('#loader').hide()

  $.ajaxSetup {
    beforeSend: (xhr) ->
      xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  }
Chezhian
fuente
5

oops ..

Perdí la siguiente línea en mi application.js

//= require jquery_ujs

Lo reemplacé y está funcionando ...

======= ACTUALIZADO =========

Después de 5 años, he vuelto con el mismo error, ahora tengo un nuevo Rails 5.1.6 y encontré esta publicación nuevamente. Al igual que el círculo de la vida.

Ahora, cuál era el problema es: Rails 5.1 eliminó la compatibilidad con jquery y jquery_ujs de forma predeterminada, y agregó

//= require rails-ujs in application.js

Hace lo siguiente:

  1. forzar cuadros de diálogo de confirmación para diversas acciones;
  2. hacer solicitudes que no sean GET desde hipervínculos;
  3. hacer formularios o hipervínculos enviar datos de forma asíncrona con Ajax;
  4. hacer que los botones de envío se deshabiliten automáticamente al enviar el formulario para evitar hacer doble clic. (de: https://github.com/rails/rails-ujs/tree/master )

Pero, ¿por qué no incluye el token csrf para la solicitud ajax? Si alguien sabe sobre esto en detalle, solo coménteme. Soy consciente de que.

De todos modos, agregué lo siguiente en mi archivo js personalizado para que funcione (Gracias por otras respuestas para ayudarme a alcanzar este código):

$( document ).ready(function() {
  $.ajaxSetup({
    headers: {
      'X-CSRF-Token': Rails.csrfToken()
    }
  });
  ----
  ----
});
Abhi
fuente
Esto era lo que necesitaba hacer también. La razón por la que esto surgió es porque estoy construyendo una aplicación React para reemplazar lentamente una aplicación Rails existente. Dado que hay un montón de ruido de JavaScript en la aplicación existente, creé un diseño diferente que accede a un archivo de JavaScript diferente, pero no pude incluirlo jquery_ujs. Ese fue el truco.
Wylliam Judd
1
Sí, a veces si nos perdemos eso cuando volvemos a trabajar, refactorice algo ... Es difícil encontrar qué va mal. Debido a que no estamos haciendo nada manualmente para que funcione, Rails lo incluye automáticamente. Creemos que ya está allí. Gracias por tales sitios sociales de Qn / Ans
Abhi
Quizás pueda responder esta pregunta similar: stackoverflow.com/questions/50159847/…
4

Si no está utilizando jQuery y está utilizando algo como buscar API para solicitudes, puede usar lo siguiente para obtener csrf-token:

document.querySelector('meta[name="csrf-token"]').getAttribute('content')

fetch('/users', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')},
    credentials: 'same-origin',
    body: JSON.stringify( { id: 1, name: 'some user' } )
    })
    .then(function(data) {
      console.log('request succeeded with JSON response', data)
    }).catch(function(error) {
      console.log('request failed', error)
    })
svnm
fuente
¿Puedo preguntarle si puede responder a esta pregunta muy similar? stackoverflow.com/questions/50159847/…
4

Utilice jquery.csrf ( https://github.com/swordray/jquery.csrf ).

  • Rieles 5.1 o posterior

    $ yarn add jquery.csrf
    //= require jquery.csrf
  • Rieles 5.0 o anteriores

    source 'https://rails-assets.org' do
      gem 'rails-assets-jquery.csrf'
    end
    //= require jquery.csrf
  • Código fuente

    (function($) {
      $(document).ajaxSend(function(e, xhr, options) {
        var token = $('meta[name="csrf-token"]').attr('content');
        if (token) xhr.setRequestHeader('X-CSRF-Token', token);
      });
    })(jQuery);

sable de espada
fuente
En 5.1 usando webpack, //= require jquery.csrfno va a funcionar, ¿verdad? En su lugar, utilicé el archivo pack js con import 'jquery.csrf'él. Tenga en cuenta que no necesita incluir esto con una etiqueta de paquete en sus vistas.
Damien Justin Šutevski
3

Si está utilizando JavaScript con jQuery para generar el token en su formulario, esto funciona:

<input name="authenticity_token" 
       type="hidden" 
       value="<%= $('meta[name=csrf-token]').attr('content') %>" />

Obviamente, necesitas tener el <%= csrf_meta_tag %>diseño en Ruby.

Tim Scollick
fuente
2

Para aquellos de ustedes que necesitan una respuesta que no sea de jQuery, simplemente agreguen lo siguiente:

xmlhttp.setRequestHeader ('X-CSRF-Token', $ ('meta [name = "csrf-token"]'). attr ('content'));

Un ejemplo muy simple se puede enviar aquí:

xmlhttp.open ("POST", "example.html", verdadero);
xmlhttp.setRequestHeader ('X-CSRF-Token', $ ('meta [name = "csrf-token"]'). attr ('content'));
xmlhttp.send ();
Mek
fuente
66
¿Esto no usa jQuery para los selectores?
Yule
2

Luché con este problema durante días. Cualquier llamada GET funcionaba correctamente, pero todos los PUT generarían un error "No se puede verificar la autenticidad del token CSRF". Mi sitio web funcionaba bien hasta que agregué un certificado SSL a nginx.

Finalmente me topé con esta línea faltante en mi configuración de nginx:

location @puma { 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    proxy_set_header Host $http_host; 
    proxy_redirect off;
    proxy_set_header X-Forwarded-Proto https;   # Needed to avoid 'WARNING: Can't verify CSRF token authenticity'
    proxy_pass http://puma; 
}

Después de agregar la línea que falta "proxy_set_header X-Fordered-Proto https;", todos mis errores de token CSRF se cierran.

Esperemos que esto ayude a alguien más que también se golpea la cabeza contra la pared. jaja

Hoffmann
fuente
0

Estoy usando Rails 4.2.4 y no podía entender por qué estaba obteniendo:

Can't verify CSRF token authenticity

Tengo en el diseño:

<%= csrf_meta_tags %>

En el controlador:

protect_from_forgery with: :exception

Invocar tcpdump -A -s 999 -i lo port 3000mostraba el encabezado que se estaba configurando (a pesar de no tener que configurar los encabezados con ajaxSetup, ya estaba hecho):

X-CSRF-Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
DNT: 1
Content-Length: 125
authenticity_token=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Al final estaba fallando porque tenía las cookies apagadas. CSRF no funciona sin las cookies habilitadas, por lo que esta es otra causa posible si está viendo este error.

Neil Stockbridge
fuente
es muy útil!
joehwang