AVERTISSEMENT: impossible de vérifier les rails d'authenticité du jeton CSRF

238

J'envoie des données de la vue au contrôleur avec AJAX et j'ai eu cette erreur:

AVERTISSEMENT: impossible de vérifier l'authenticité du jeton CSRF

Je pense que je dois envoyer ce jeton avec des données.

Est-ce que quelqu'un sait comment faire cela?

Edit: Ma solution

J'ai fait cela en mettant le code suivant dans le message AJAX:

headers: {
  'X-Transaction': 'POST Example',
  'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
kbaccouche
la source
7
avez-vous <% = csrf_meta_tag%> dans votre en-tête de mise en page?
Anatoly
oui comme ceci: <% = csrf_meta_tags%>
kbaccouche
6
avez-vous des bibliothèques jquery-rails qui fournissent des fonctionnalités côté client ajax?
Anatoly
2
Et la façon HAML est d'ajouter "= csrf_meta_tags"
Ege Akpinar
belle question, merci d'avoir posé la question
AMIC MING

Réponses:

374

Vous devriez faire ceci:

  1. Assurez-vous que vous avez <%= csrf_meta_tag %>dans votre mise en page

  2. Ajoutez beforeSendà toutes les requêtes ajax pour définir l'en-tête comme ci-dessous:


$.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);
  }
});

Pour envoyer un jeton dans toutes les demandes, vous pouvez utiliser:

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
  }
});
Chau Hong Linh
la source
5
Merci! A fonctionné pour moi comme un charme!
Misha Moroshko
35
La bibliothèque jQuery UJS fournie par l'équipe Rails ajoute automatiquement le jeton CSRF à la demande jQuery AJAX. Le fichier README contient des instructions sur la configuration. github.com/rails/jquery-ujs/blob/master/src/rails.js#L91
James Conroy-Finn
1
Notez que vous pouvez définir l'en-tête pour toutes les demandes à la fois avec la fonction $ .ajaxSetup.
Pieter Jongsma
1
Excellent! Cherche depuis un certain temps pour cette réponse. Fonctionne parfaitement. Merci!
cassi.lup
4
Notez que si vous utilisez jQuery UJS comme suggéré ci-dessus, vous devez vous assurer que l'inclusion de rails-ujs vient après l'inclusion de jquery ou il échouera avec la même erreur que l'op.
Topher Fangio
31

La meilleure façon de le faire est en fait simplement d'utiliser <%= form_authenticity_token.to_s %>pour imprimer le jeton directement dans votre code de rails. Vous n'avez pas besoin d'utiliser javascript pour rechercher le token csrf dans le dom comme d'autres articles le mentionnent. ajoutez simplement l'option en-têtes comme ci-dessous;

$.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) %>"
})
ADAM
la source
7
Au lieu de faire cela pour chaque commande ajax, vous pouvez ajouter des en- têtes à $ .ajaxSetup () .
Scott McMillin
1
Je recommanderais plutôt d'utiliser cette réponse ...
opsidao
14
Je n'aime pas vraiment l'approche de l'utilisation d'ERB dans le javascript.
radixhound
Cela vous oblige à générer votre javascript avec ERB, ce qui est très limitant. Même s'il y a des endroits où ERB pourrait être un bon choix, il y en a d'autres où il ne l'est pas, et l'ajouter juste pour obtenir le jeton serait un gaspillage.
sockmonk
22

Si je me souviens bien, vous devez ajouter le code suivant à votre formulaire, pour vous débarrasser de ce problème:

<%= token_tag(nil) %>

N'oubliez pas le paramètre.

auralbee
la source
8
En fait, cela devrait être: <%= token_tag(nil) %>. Ensuite, vous obtenez le jeton généré automatiquement.
szeryf
15

En effet le moyen le plus simple. Ne vous embêtez pas à changer les en-têtes.

Assurez-vous que vous avez:

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

Faites simplement un champ de saisie caché comme ceci:

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

Ou si vous voulez un article 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
la source
L'ajout du champ de saisie masqué à mon formulaire de saisie a résolu le problème pour moi.
Teemu Leisti
cela se fait automatiquement si vous utilisez les assistants de formulaire de Rails.
Jason FB
13

La mise à niveau d'une ancienne application vers rails 3.1, y compris la balise Meta csrf ne le résout toujours pas. Sur le blog rubyonrails.org, ils donnent quelques conseils de mise à niveau, et en particulier cette ligne de jquery qui devrait aller dans la section tête de votre mise en page:

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

extrait de cet article de blog: http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails .

Dans mon cas, la session était réinitialisée à chaque demande ajax. L'ajout du code ci-dessus a résolu ce problème.

Danny
la source
10
  1. Assurez-vous que vous avez <%= csrf_meta_tag %>dans votre mise en page
  2. Ajoutez un beforeSendpour inclure le jeton csrf dans la demande ajax pour définir l'en-tête. Ceci n'est requis que pour les postdemandes.

Le code pour lire le jeton csrf est disponible dans le rails/jquery-ujs, donc à mon humble avis, il est plus facile de simplement l'utiliser, comme suit:

$.ajax({
  url: url,
  method: 'post',
  beforeSend: $.rails.CSRFProtection,
  data: {
    // ...
  }
})
nathanvda
la source
Simple sans réimplémenter ce qui est déjà inclus par Rails. Ce devrait être la réponse choisie.
jiehanzheng
Dans Rails 5.1 fonctionne également:headers: { 'X-CSRF-Token': Rails.csrfToken() }
olhor
@nathanvda, vous pouvez peut-être répondre à cette question similaire: stackoverflow.com/questions/50159847/…
6

Les réponses les plus votées ici sont correctes mais ne fonctionneront pas si vous effectuez des demandes interdomaines car la session ne sera pas disponible à moins que vous ne demandiez explicitement à jQuery de passer le cookie de session. Voici comment procéder:

$.ajax({ 
  url: url,
  type: 'POST',
  beforeSend: function(xhr) {
    xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  },
  xhrFields: {
    withCredentials: true
  }
});
dreadwail
la source
Puis-je vous demander si vous pouvez répondre à cette question très similaire? stackoverflow.com/questions/50159847/…
5

Vous pouvez l'écrire globalement comme ci-dessous.

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'))}
    });
});

Script 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
la source
5

Oups..

J'ai raté la ligne suivante dans mon application.js

//= require jquery_ujs

Je l'ai remplacé et son fonctionnement ..

======= MISE À JOUR =========

Après 5 ans, je suis de retour avec la même erreur, maintenant j'ai de nouveaux Rails 5.1.6 , et j'ai retrouvé ce post. Tout comme le cercle de la vie.

Maintenant, quel était le problème: Rails 5.1 a supprimé la prise en charge de jquery et jquery_ujs par défaut, et a ajouté

//= require rails-ujs in application.js

Il fait les choses suivantes:

  1. forcer les dialogues de confirmation pour diverses actions;
  2. faire des demandes non GET à partir d'hyperliens;
  3. faire des formulaires ou des hyperliens soumettre des données de manière asynchrone avec Ajax;
  4. les boutons d'envoi sont automatiquement désactivés sur le formulaire d'envoi pour éviter un double-clic. (à partir de: https://github.com/rails/rails-ujs/tree/master )

Mais pourquoi n'inclut-il pas le jeton csrf pour la demande ajax? Si quelqu'un le sait en détail, commentez-moi. J'apprécie ça.

Quoi qu'il en soit, j'ai ajouté ce qui suit dans mon fichier js personnalisé pour le faire fonctionner (merci pour les autres réponses pour m'aider à atteindre ce code):

$( document ).ready(function() {
  $.ajaxSetup({
    headers: {
      'X-CSRF-Token': Rails.csrfToken()
    }
  });
  ----
  ----
});
Abhi
la source
C'était aussi ce que je devais faire. La raison pour laquelle cela est apparu est que je construis une application React pour remplacer lentement une application Rails existante. Puisqu'il y a beaucoup de bruit javascript dans l'application existante, j'ai créé une disposition différente qui accède à un fichier javascript différent, mais je n'ai pas réussi à l'inclure jquery_ujs. C'était ça l'astuce.
Wylliam Judd
1
Oui, parfois Si nous manquons cela lors de la reprise, refactoriser quelque chose .. Il est difficile de trouver ce qui ne va pas. Parce que nous ne faisons rien manuellement pour le faire fonctionner, Rails l'inclut automatiquement. Nous le pensons déjà là. Merci pour ces sites sociaux Qn / Ans
Abhi
Vous pouvez peut-être répondre à cette question similaire: stackoverflow.com/questions/50159847/…
4

Si vous n'utilisez pas jQuery et utilisez quelque chose comme fetch API pour les requêtes, vous pouvez utiliser ce qui suit pour obtenir 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
la source
Puis-je vous demander si vous pouvez répondre à cette question très similaire? stackoverflow.com/questions/50159847/…
4

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

  • Rails 5.1 ou version ultérieure

    $ yarn add jquery.csrf
    //= require jquery.csrf
  • Rails 5.0 ou avant

    source 'https://rails-assets.org' do
      gem 'rails-assets-jquery.csrf'
    end
    //= require jquery.csrf
  • Code source

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

épée
la source
En 5.1 utilisant webpack, ça //= require jquery.csrfne marchera pas, non?. Au lieu de cela, j'ai utilisé le fichier pack js avec import 'jquery.csrf'. Notez que vous n'avez pas besoin de l'inclure avec une balise pack dans vos vues.
Damien Justin Šutevski
3

Si vous utilisez javascript avec jQuery pour générer le jeton dans votre formulaire, cela fonctionne:

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

De toute évidence, vous devez avoir le <%= csrf_meta_tag %>dans votre disposition Ruby.

Tim Scollick
la source
2

Pour ceux d'entre vous qui ont besoin d'une réponse non jQuery, vous pouvez simplement ajouter ce qui suit:

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

Un exemple très simple peut être trouvé ici:

xmlhttp.open ("POST", "example.html", true);
xmlhttp.setRequestHeader ('X-CSRF-Token', $ ('meta [name = "csrf-token"]'). attr ('content'));
xmlhttp.send ();
Mek
la source
6
Cela n'utilise-t-il pas jQuery pour les sélecteurs?
Yule
2

J'ai eu du mal avec ce problème pendant des jours. Tout appel GET fonctionnait correctement, mais tous les PUT génèreraient une erreur «Impossible de vérifier l'authenticité du jeton CSRF». Mon site Web fonctionnait bien jusqu'à ce que j'aie ajouté un certificat SSL à nginx.

Je suis finalement tombé sur cette ligne manquante dans mes paramètres 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; 
}

Après avoir ajouté la ligne manquante "proxy_set_header X-Forwarded-Proto https;", toutes mes erreurs de jeton CSRF se terminent.

J'espère que cela aide quelqu'un d'autre qui se bat aussi la tête contre un mur. haha

Hoffmann
la source
0

J'utilise Rails 4.2.4 et je n'ai pas pu comprendre pourquoi je recevais:

Can't verify CSRF token authenticity

J'ai dans la mise en page:

<%= csrf_meta_tags %>

Dans le contrôleur:

protect_from_forgery with: :exception

Invoquer tcpdump -A -s 999 -i lo port 3000montrait l'en-tête en cours de définition (bien ajaxSetupqu'il n'ait pas besoin de définir les en-têtes avec - c'était déjà fait):

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

En fin de compte, cela a échoué car j'avais désactivé les cookies. CSRF ne fonctionne pas sans l'activation des cookies, c'est donc une autre cause possible si vous voyez cette erreur.

Neil Stockbridge
la source
c'est très utile!
joehwang