PERINGATAN: Tidak dapat memverifikasi rel token keaslian CSRF

238

Saya mengirim data dari tampilan ke pengontrol dengan AJAX dan saya mendapat kesalahan ini:

PERINGATAN: Tidak dapat memverifikasi keaslian token CSRF

Saya pikir saya harus mengirim token ini dengan data.

Adakah yang tahu bagaimana saya bisa melakukan ini?

Sunting: Solusi saya

Saya melakukan ini dengan meletakkan kode berikut di dalam posting AJAX:

headers: {
  'X-Transaction': 'POST Example',
  'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
kbaccouche
sumber
7
Apakah Anda memiliki <% = csrf_meta_tag%> di header tata letak Anda?
Anatoly
ya seperti ini: <% = csrf_meta_tags%>
kbaccouche
6
apakah Anda memiliki jquery-rails libraries yang menyediakan fungsionalitas sisi klien ajax?
Anatoly
2
Dan cara HAML adalah menambahkan "= csrf_meta_tags"
Ege Akpinar
pertanyaan yang bagus, terima kasih telah bertanya
AMIC MING

Jawaban:

374

Anda harus melakukan ini:

  1. Pastikan Anda memiliki <%= csrf_meta_tag %>tata letak Anda

  2. Tambahkan beforeSendke semua permintaan ajax untuk mengatur header seperti di bawah ini:


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

Untuk mengirim token di semua permintaan, Anda dapat menggunakan:

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
  }
});
Chau Hong Linh
sumber
5
Terima kasih! Bekerja untuk saya seperti pesona!
Misha Moroshko
35
Perpustakaan jQuery UJS yang disediakan oleh tim Rails menambahkan token CSRF ke permintaan jQuery AJAX secara otomatis. README berisi instruksi tentang cara mendapatkan pengaturan. github.com/rails/jquery-ujs/blob/master/src/rails.js#L91
James Conroy-Finn
1
Perhatikan bahwa Anda dapat mengatur tajuk untuk semua permintaan sekaligus dengan fungsi $ .ajaxSetup.
Pieter Jongsma
1
Luar biasa! Sedang mencari jawaban ini sebentar. Bekerja dengan lancar. Terima kasih!
cassi.lup
4
Sebagai catatan, jika Anda menggunakan jQuery UJS seperti yang disarankan di atas, Anda perlu memastikan bahwa rails-ujs include datang setelah jquery include atau akan gagal dengan kesalahan yang sama dengan op.
Topher Fangio
31

Cara terbaik untuk melakukan ini sebenarnya hanya digunakan <%= form_authenticity_token.to_s %>untuk mencetak token langsung di kode rel Anda. Anda tidak perlu menggunakan javascript untuk mencari dom untuk csrf token seperti yang disebutkan posting lainnya. cukup tambahkan opsi header seperti di bawah ini;

$.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
sumber
7
Daripada melakukan ini untuk setiap perintah ajax, Anda bisa menambahkan header ke $ .ajaxSetup () .
Scott McMillin
1
Saya lebih suka menggunakan jawaban ini ...
opsidao
14
Saya tidak terlalu suka pendekatan menggunakan ERB di javascript.
radixhound
Ini memaksa Anda untuk menghasilkan javascript dengan ERB, yang sangat membatasi. Bahkan jika ada tempat-tempat di mana ERB mungkin cocok, ada tempat-tempat lain di mana tidak, dan menambahkannya hanya untuk mendapatkan token akan sia-sia.
sockmonk
22

Jika saya ingat dengan benar, Anda harus menambahkan kode berikut ke formulir Anda, untuk menghilangkan masalah ini:

<%= token_tag(nil) %>

Jangan lupa parameternya.

Auralbee
sumber
8
Sebenarnya, ini harus: <%= token_tag(nil) %>. Kemudian Anda mendapatkan token yang dibuat secara otomatis.
szeryf
15

Memang cara paling sederhana. Jangan repot-repot mengganti header.

Pastikan Anda memiliki:

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

Lakukan saja bidang input tersembunyi seperti:

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

Atau jika Anda ingin posting 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
sumber
Menambahkan bidang input tersembunyi ke formulir input saya memecahkan masalah bagi saya.
Teemu Leisti
ini dilakukan secara otomatis jika Anda menggunakan formulir bantuan Rails.
Jason FB
13

Peningkatan versi dari aplikasi yang lebih lama ke rails 3.1, termasuk meta tag csrf masih belum menyelesaikannya. Di blog rubyonrails.org, mereka memberikan beberapa kiat pemutakhiran, dan khususnya jquery baris ini yang seharusnya ada di bagian kepala tata letak Anda:

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

diambil dari posting blog ini: http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails .

Dalam kasus saya, sesi sedang diatur ulang pada setiap permintaan ajax. Menambahkan kode di atas menyelesaikan masalah itu.

Danny
sumber
10
  1. Pastikan Anda memiliki <%= csrf_meta_tag %>tata letak Anda
  2. Tambahkan a beforeSenduntuk memasukkan csrf-token dalam permintaan ajax untuk mengatur header. Ini hanya diperlukan untuk postpermintaan.

Kode untuk membaca token csrf tersedia di rails/jquery-ujs, jadi imho paling mudah untuk hanya menggunakan itu, sebagai berikut:

$.ajax({
  url: url,
  method: 'post',
  beforeSend: $.rails.CSRFProtection,
  data: {
    // ...
  }
})
nathanvda
sumber
Sederhana tanpa menerapkan kembali apa yang sudah disertakan oleh Rails. Ini harus menjadi jawaban yang dipilih.
jiehanzheng
Dalam Rails 5.1 juga berfungsi:headers: { 'X-CSRF-Token': Rails.csrfToken() }
olhor
@nathanvda, Mungkin Anda dapat menjawab pertanyaan serupa: stackoverflow.com/questions/50159847/…
6

Jawaban pilihan teratas di sini sudah benar tetapi tidak akan berfungsi jika Anda melakukan permintaan lintas-domain karena sesi tidak akan tersedia kecuali jika Anda secara eksplisit memberi tahu jQuery untuk meneruskan cookie sesi. Berikut cara melakukannya:

$.ajax({ 
  url: url,
  type: 'POST',
  beforeSend: function(xhr) {
    xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  },
  xhrFields: {
    withCredentials: true
  }
});
rambut gimbal
sumber
Bolehkah saya bertanya kepada Anda jika Anda dapat menjawab pertanyaan yang sangat mirip ini? stackoverflow.com/questions/50159847/...
5

Anda dapat menulisnya secara global seperti di bawah ini.

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

Naskah Kopi:

  $('#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
sumber
5

Ups ..

Saya melewatkan baris berikut di application.js saya

//= require jquery_ujs

Saya menggantinya dan berfungsi ..

======= DIPERBARUI =========

Setelah 5 tahun, saya kembali dengan kesalahan yang sama, sekarang saya memiliki Rails baru 5.1.6 , dan saya menemukan posting ini lagi. Persis seperti lingkaran kehidupan.

Sekarang yang menjadi masalah adalah: Rails 5.1 menghapus dukungan untuk jquery dan jquery_ujs secara default, dan ditambahkan

//= require rails-ujs in application.js

Itu melakukan hal-hal berikut:

  1. paksa dialog konfirmasi untuk berbagai tindakan;
  2. membuat permintaan non-GET dari hyperlink;
  3. membuat formulir atau hyperlink mengirimkan data secara tidak sinkron dengan Ajax;
  4. punya tombol kirim menjadi otomatis dinonaktifkan pada formulir kirim untuk mencegah mengklik dua kali. (dari: https://github.com/rails/rails-ujs/tree/master )

Tapi mengapa itu tidak termasuk token csrf untuk permintaan ajax? Jika ada yang tahu tentang ini secara detail, beri komentar saja. Saya menghargai itu.

Lagi pula saya menambahkan yang berikut ini di file js khusus saya untuk membuatnya berfungsi (Terima kasih atas jawaban lain untuk membantu saya mencapai kode ini):

$( document ).ready(function() {
  $.ajaxSetup({
    headers: {
      'X-CSRF-Token': Rails.csrfToken()
    }
  });
  ----
  ----
});
Abhi
sumber
Inilah yang perlu saya lakukan juga. Alasan ini muncul adalah karena saya sedang membangun Aplikasi Bereaksi untuk secara perlahan mengganti Aplikasi Rails yang ada. Karena ada banyak suara javascript yang terjadi di aplikasi yang ada, saya membuat tata letak yang berbeda yang mengakses file javascript yang berbeda, tetapi saya gagal memasukkannya jquery_ujs. Itulah triknya.
Wylliam Judd
1
Ya, Terkadang Jika kami melewatkannya saat pengerjaan ulang, refactor sesuatu..Sangat sulit menemukan apa yang salah. Karena kami tidak melakukan apa pun secara manual untuk membuatnya bekerja, Rails secara otomatis memasukkannya. Kami pikir sudah ada di sana. Terima kasih untuk Situs Qn / Ans sosial seperti itu
Abhi
Mungkin Anda dapat menjawab pertanyaan serupa ini: stackoverflow.com/questions/50159847/...
4

Jika Anda tidak menggunakan jQuery dan menggunakan sesuatu seperti mengambil API untuk permintaan, Anda dapat menggunakan yang berikut ini untuk mendapatkan 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
sumber
Bolehkah saya bertanya kepada Anda jika Anda dapat menjawab pertanyaan yang sangat mirip ini? stackoverflow.com/questions/50159847/...
4

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

  • Rel 5.1 atau yang lebih baru

    $ yarn add jquery.csrf
    //= require jquery.csrf
  • Rails 5.0 atau sebelumnya

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

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

pedang pendek
sumber
Di 5.1 menggunakan webpack, //= require jquery.csrftidak akan berfungsi, kan ?. Sebaliknya saya menggunakan file paket js dengan import 'jquery.csrf'di dalamnya. Catatan Anda tidak perlu menyertakan ini dengan tag paket dalam tampilan Anda.
Damien Justin Šutevski
3

Jika Anda menggunakan javascript dengan jQuery untuk menghasilkan token di formulir Anda, ini berfungsi:

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

Jelas, Anda harus memiliki <%= csrf_meta_tag %>tata letak Ruby Anda.

Tim Scollick
sumber
2

Bagi Anda yang membutuhkan jawaban non jQuery, Anda dapat dengan mudah menambahkan yang berikut:

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

Contoh yang sangat sederhana dapat dilihat di sini:

xmlhttp.open ("POST", "example.html", true);
xmlhttp.setRequestHeader ('X-CSRF-Token', $ ('meta [name = "csrf-token"]'). attr ('konten'));
xmlhttp.send ();
Mek
sumber
6
Apakah ini tidak menggunakan jQuery untuk penyeleksi?
Yule
2

Saya berjuang dengan masalah ini selama berhari-hari. Setiap panggilan GET berfungsi dengan benar, tetapi semua PUT akan menghasilkan kesalahan "Tidak dapat memverifikasi keaslian token CSRF". Situs web saya berfungsi dengan baik sampai saya menambahkan sertifikat SSL ke nginx.

Saya akhirnya menemukan garis yang hilang ini di pengaturan nginx saya:

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

Setelah menambahkan baris yang hilang "proxy_set_header X-Forwarded-Proto https;", semua kesalahan token CSRF saya berhenti.

Semoga ini bisa membantu orang lain yang juga membenturkan kepalanya ke dinding. ha ha

Hoffmann
sumber
0

Saya menggunakan Rails 4.2.4 dan tidak tahu mengapa saya mendapatkan:

Can't verify CSRF token authenticity

Saya miliki dalam tata letak:

<%= csrf_meta_tags %>

Di controller:

protect_from_forgery with: :exception

Memanggil tcpdump -A -s 999 -i lo port 3000menunjukkan tajuk sedang diset (meskipun tidak perlu mengatur tajuk dengan ajaxSetup- sudah dilakukan):

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

Pada akhirnya itu gagal karena cookie saya dimatikan. CSRF tidak berfungsi tanpa cookie diaktifkan, jadi ini kemungkinan penyebab lain jika Anda melihat kesalahan ini.

Neil Stockbridge
sumber
ini sangat membantu!
joehwang