OSTRZEŻENIE: Nie można zweryfikować szyn autentyczności tokenów CSRF

238

Przesyłam dane z widoku do kontrolera za pomocą AJAX i otrzymałem ten błąd:

OSTRZEŻENIE: Nie można zweryfikować autentyczności tokenu CSRF

Myślę, że muszę wysłać ten token z danymi.

Czy ktoś wie jak to zrobić?

Edycja: Moje rozwiązanie

Zrobiłem to, umieszczając następujący kod w poście AJAX:

headers: {
  'X-Transaction': 'POST Example',
  'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
kbaccouche
źródło
7
czy masz <% = csrf_meta_tag%> w nagłówku układu?
Anatolij
tak jak to: <% = csrf_meta_tags%>
kbaccouche
6
czy masz biblioteki jquery-rails, które zapewniają funkcjonalność ajax po stronie klienta?
Anatolij
2
A metodą HAML jest dodanie „= csrf_meta_tags”
Ege Akpinar
fajne pytanie, dziękuję za pytanie
AMIC MING,

Odpowiedzi:

374

Powinieneś to zrobić:

  1. Upewnij się, że masz <%= csrf_meta_tag %>w swoim układzie

  2. Dodaj beforeSenddo wszystkich żądań ajax, aby ustawić nagłówek jak poniżej:


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

Aby wysłać token we wszystkich żądaniach, możesz użyć:

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
  }
});
Chau Hong Linh
źródło
5
Dzięki! Pracował dla mnie jak urok!
Misha Moroshko
35
Biblioteka jQuery UJS dostarczona przez zespół Railsów automatycznie dodaje token CSRF do żądania AJAX jQuery. Plik README zawiera instrukcje dotyczące konfiguracji. github.com/rails/jquery-ujs/blob/master/src/rails.js#L91
James Conroy-Finn
1
Pamiętaj, że możesz ustawić nagłówek dla wszystkich żądań jednocześnie za pomocą funkcji $ .ajaxSetup.
Pieter Jongsma
1
Doskonały! Trochę szukałem tej odpowiedzi. Działa płynnie. Dzięki!
cassi.lup
4
Na marginesie, jeśli używasz jQuery UJS jak zasugerowano powyżej, należy się upewnić, że szyny-ujs to przychodzi po jQuery zawierać lub nie powiedzie się z powodu tego samego błędu co op.
Topher Fangio,
31

Najlepszym sposobem na to jest po prostu <%= form_authenticity_token.to_s %>wydrukowanie tokena bezpośrednio w kodzie szyny. Nie musisz używać javascript do przeszukiwania domeny w poszukiwaniu tokena csrf, jak wspominają inne posty. po prostu dodaj opcję nagłówków, jak poniżej;

$.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
źródło
7
Zamiast robić to dla każdej komendy ajax, możesz dodać nagłówki do $ .ajaxSetup () .
Scott McMillin
1
Wolałbym raczej użyć tej odpowiedzi ...
opsidao,
14
Nie podoba mi się podejście do używania ERB w javascript.
radixhound
To zmusza cię do wygenerowania javascript za pomocą ERB, co jest bardzo ograniczające. Nawet jeśli istnieją miejsca, w których ERB może być dobrym dopasowaniem, są też inne, w których nie ma takiego rozwiązania, a dodanie go tylko po to, aby uzyskać token, byłoby marnotrawstwem.
sockmonk
22

Jeśli dobrze pamiętam, musisz dodać następujący kod do formularza, aby pozbyć się tego problemu:

<%= token_tag(nil) %>

Nie zapomnij parametru.

auralbee
źródło
8
Właściwie to powinno być: <%= token_tag(nil) %>. Następnie otrzymasz automatycznie wygenerowany token.
szeryf
15

Rzeczywiście najprostszy sposób. Nie zawracaj sobie głowy zmienianiem nagłówków.

Upewnij się że masz:

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

Po prostu wykonaj ukryte pole wejściowe w następujący sposób:

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

Lub jeśli chcesz postu 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
źródło
Dodanie ukrytego pola wejściowego do formularza wejściowego rozwiązało problem.
Teemu Leisti
dzieje się to automatycznie, jeśli używasz pomocników formularzy Railsów.
Jason FB
13

Uaktualnienie ze starszej aplikacji do szyn 3.1, w tym metatag csrf, nadal go nie rozwiązuje. Na blogu rubyonrails.org podają kilka wskazówek dotyczących aktualizacji, aw szczególności tej linii jquery, która powinna znaleźć się w sekcji head twojego układu:

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

zaczerpnięte z tego postu na blogu: http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails .

W moim przypadku sesja była resetowana przy każdym żądaniu ajax. Dodanie powyższego kodu rozwiązało ten problem.

Danny
źródło
10
  1. Upewnij się, że masz <%= csrf_meta_tag %>w swoim układzie
  2. Dodaj a, beforeSendaby dołączyć token csrf do żądania ajax, aby ustawić nagłówek. Jest to wymagane tylko w przypadku postwniosków.

Kod do odczytu tokena csrf jest dostępny w rails/jquery-ujs, więc imho najłatwiej jest po prostu z niego skorzystać w następujący sposób:

$.ajax({
  url: url,
  method: 'post',
  beforeSend: $.rails.CSRFProtection,
  data: {
    // ...
  }
})
nathanvda
źródło
Proste bez ponownego wdrażania tego, co już zawiera Rails. To powinna być wybrana odpowiedź.
jiehanzheng
W Rails 5.1 działa również:headers: { 'X-CSRF-Token': Rails.csrfToken() }
olhor
@nathanvda, może możesz odpowiedzieć na podobne pytanie: stackoverflow.com/questions/50159847/…
6

Najczęściej głosowane odpowiedzi tutaj są poprawne, ale nie będą działać, jeśli wykonujesz żądania między domenami, ponieważ sesja nie będzie dostępna, chyba że wyraźnie powiesz jQuery, aby przekazała sesyjny plik cookie. Oto jak to zrobić:

$.ajax({ 
  url: url,
  type: 'POST',
  beforeSend: function(xhr) {
    xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  },
  xhrFields: {
    withCredentials: true
  }
});
dreadwail
źródło
Czy mogę zapytać, czy możesz odpowiedzieć na to bardzo podobne pytanie? stackoverflow.com/questions/50159847/…
5

Możesz napisać to globalnie, jak poniżej.

Normalny JS:

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

Skrypt kawy:

  $('#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
źródło
5

ups ..

W moim pliku application.js brakuje mi następującego wiersza

//= require jquery_ujs

Wymieniłem go i działa.

======= AKTUALIZACJA =========

Po 5 latach wróciłem z tym samym błędem, teraz mam zupełnie nowe Railsy 5.1.6 i ponownie znalazłem ten post. Zupełnie jak krąg życia.

Problem polegał na tym, że Rails 5.1 domyślnie usunął obsługę jquery i jquery_ujs i dodał

//= require rails-ujs in application.js

Robi następujące rzeczy:

  1. wymuś okna dialogowe potwierdzania różnych działań;
  2. wysyłać żądania inne niż GET z hiperłączy;
  3. zmusza formularze lub hiperłącza do przesyłania danych asynchronicznie za pomocą Ajax;
  4. przyciski przesyłania są automatycznie wyłączane w formularzu przesyłania, aby zapobiec podwójnemu kliknięciu. (od: https://github.com/rails/rails-ujs/tree/master )

Ale dlaczego nie zawiera tokena csrf dla żądania ajax? Jeśli ktoś wie o tym szczegółowo, po prostu skomentuj mnie. Doceniam to.

W każdym razie dodałem następujące elementy do mojego niestandardowego pliku js, aby działał (dziękuję za inne odpowiedzi, które pomogą mi dotrzeć do tego kodu):

$( document ).ready(function() {
  $.ajaxSetup({
    headers: {
      'X-CSRF-Token': Rails.csrfToken()
    }
  });
  ----
  ----
});
Abhi
źródło
Tego też musiałem zrobić. Powodem tego jest to, że tworzę aplikację React, aby powoli zastępować istniejącą aplikację Rails. Ponieważ w istniejącej aplikacji dzieje się mnóstwo szumów javascript, stworzyłem inny układ, który uzyskuje dostęp do innego pliku javascript, ale nie udało mi się go włączyć jquery_ujs. To była sztuczka.
Wylliam Judd
1
Tak, czasami Jeśli przeoczymy to podczas przeróbki, przebuduj coś… Trudno jest znaleźć, co się dzieje. Ponieważ nie robimy nic ręcznie, aby działało, Railsy automatycznie to uwzględniają. Myślimy, że już tam jest. Dzięki za takie społecznościowe strony Qn / Ans
Abhi
Może możesz odpowiedzieć na podobne pytanie: stackoverflow.com/questions/50159847/…
4

Jeśli nie używasz jQuery i używasz czegoś takiego jak API pobierania dla żądań, możesz użyć następujących opcji, aby uzyskać 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
źródło
Czy mogę zapytać, czy możesz odpowiedzieć na to bardzo podobne pytanie? stackoverflow.com/questions/50159847/…
4

Użyj jquery.csrf ( https://github.com/swordray/jquery.csrf ).

  • Rails 5.1 lub nowszy

    $ yarn add jquery.csrf
    //= require jquery.csrf
  • Railsy 5.0 lub wcześniejsze

    source 'https://rails-assets.org' do
      gem 'rails-assets-jquery.csrf'
    end
    //= require jquery.csrf
  • Kod źródłowy

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

miecznik
źródło
W 5.1 za pomocą webpacka //= require jquery.csrfnie zadziała, prawda ?. Zamiast tego użyłem import 'jquery.csrf'w nim pliku js paczki . Pamiętaj, że nie musisz dołączać tego do tagu pack w swoich widokach.
Damien Justin Šutevski
3

Jeśli używasz javascript z jQuery do generowania tokena w formularzu, działa to:

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

Oczywiście musisz mieć <%= csrf_meta_tag %>w swoim układzie Ruby.

Tim Scollick
źródło
2

Dla tych z Was, którzy potrzebują odpowiedzi innej niż jQuery, możesz po prostu dodać:

xmlhttp.setRequestHeader („Token X-CSRF”, $ („meta [nazwa =„ token csrf ”]”). attr („content”));

Bardzo prosty przykład może być tutaj:

xmlhttp.open („POST”, „example.html”, true);
xmlhttp.setRequestHeader („Token X-CSRF”, $ („meta [nazwa =„ token csrf ”]”). attr („content”));
xmlhttp.send ();
Mek
źródło
6
Czy to nie używa jQuery dla selektorów?
Yule
2

Walczyłem z tym problemem przez wiele dni. Każde wywołanie GET działało poprawnie, ale wszystkie PUT generowałyby błąd „Nie można zweryfikować autentyczności tokenu CSRF”. Moja strona działała dobrze, dopóki nie dodałem certyfikatu SSL do nginx.

W końcu natknąłem się na tę brakującą linię w moich ustawieniach 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; 
}

Po dodaniu brakującej linii „proxy_set_header X-Forwarded-Proto https;” wszystkie moje tokeny CSRF zostały zamknięte.

Mam nadzieję, że pomoże to komuś, kto również uderza głową o ścianę. ha ha

Hoffmann
źródło
0

Korzystam z Rails 4.2.4 i nie mogłem zrozumieć, dlaczego otrzymałem:

Can't verify CSRF token authenticity

Mam w układzie:

<%= csrf_meta_tags %>

W kontrolerze:

protect_from_forgery with: :exception

Wywołanie tcpdump -A -s 999 -i lo port 3000pokazywało ustawiony nagłówek (mimo że nie trzeba ustawiać nagłówków za pomocą ajaxSetup- zostało to już zrobione):

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

W końcu się nie udawało, ponieważ miałem wyłączone pliki cookie. CSRF nie działa bez włączonych plików cookie, więc jest to kolejna możliwa przyczyna tego błędu.

Neil Stockbridge
źródło
to bardzo pomaga!
joehwang