คำเตือน: ไม่สามารถตรวจสอบรางความถูกต้องของโทเค็น CSRF


238

ฉันกำลังส่งข้อมูลจากมุมมองถึงตัวควบคุมด้วย AJAX และฉันได้รับข้อผิดพลาดนี้:

คำเตือน: ไม่สามารถตรวจสอบความถูกต้องของโทเค็น CSRF

ฉันคิดว่าฉันต้องส่งโทเค็นนี้พร้อมข้อมูล

ไม่มีใครรู้ว่าฉันจะทำสิ่งนี้ได้อย่างไร

แก้ไข: โซลูชันของฉัน

ฉันทำได้โดยใส่รหัสต่อไปนี้ในโพสต์ AJAX:

headers: {
  'X-Transaction': 'POST Example',
  'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},

7
คุณมี<% = csrf_meta_tag%>ในส่วนหัวเค้าโครงของคุณหรือไม่
Anatoly

ใช่เช่นนี้: <% = csrf_meta_tags%>
kbaccouche

6
คุณมีห้องสมุดjquery-railsที่มีฟังก์ชั่นฝั่งไคลเอ็นต์ ajax หรือไม่?
Anatoly

2
และวิธี HAML คือการเพิ่ม "= csrf_meta_tags"
Ege Akpinar

เป็นคำถามที่ดีขอบคุณที่ถาม
AMIC MING

คำตอบ:


374

คุณควรทำสิ่งนี้:

  1. ตรวจสอบให้แน่ใจว่าคุณมี<%= csrf_meta_tag %>ในเค้าโครงของคุณ

  2. เพิ่มbeforeSendลงในคำขอ ajax ทั้งหมดเพื่อตั้งค่าส่วนหัวดังนี้:


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

ในการส่งโทเค็นในคำขอทั้งหมดคุณสามารถใช้:

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
  }
});

5
ขอบคุณ! ทำงานให้ฉันเหมือนมีเสน่ห์!
Misha Moroshko

35
ไลบรารี jQuery UJS จัดทำโดยทีม Rails เพิ่มโทเค็น CSRF ลงในคำขอ jQuery AJAX โดยอัตโนมัติ README มีคำแนะนำเกี่ยวกับวิธีการตั้งค่า github.com/rails/jquery-ujs/blob/master/src/rails.js#L91
James Conroy-Finn

1
โปรดทราบว่าคุณสามารถตั้งค่าส่วนหัวสำหรับคำขอทั้งหมดได้พร้อมกันด้วยฟังก์ชัน $ .ajaxSetup
Pieter Jongsma

1
ยอดเยี่ยม เคยค้นหาคำตอบนี้สักครู่ ทำงานได้อย่างราบรื่น ขอบคุณ!
cassi.lup

4
โปรดทราบว่าถ้าคุณใช้ jQuery UJS ตามที่แนะนำข้างต้นคุณต้องแน่ใจว่า Rails-ujs include มาหลังจาก jquery include ไม่เช่นนั้นจะล้มเหลวโดยมีข้อผิดพลาดเดียวกับ op
Topher Fangio

31

วิธีที่ดีที่สุดในการทำเช่นนี้คือใช้<%= form_authenticity_token.to_s %>เพื่อพิมพ์โทเค็นโดยตรงในรหัสรางของคุณ คุณไม่จำเป็นต้องใช้จาวาสคริปต์เพื่อค้นหาโทเค็นสำหรับโทเค็น csrf ตามที่โพสต์อื่น ๆ พูดถึง เพียงเพิ่มตัวเลือกส่วนหัวดังต่อไปนี้

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

7
แทนการทำเช่นนี้สำหรับแต่ละคำสั่ง AJAX, คุณสามารถเพิ่มส่วนหัวเพื่อ$ .ajaxSetup ()
Scott McMillin

1
ฉันอยากจะแนะนำให้ใช้คำตอบนี้ ...
opsidao

14
ฉันไม่ชอบวิธีการใช้ ERB ในจาวาสคริปต์
radixhound

สิ่งนี้บังคับให้คุณสร้างจาวาสคริปต์ด้วย ERB ซึ่งมีข้อ จำกัด มาก แม้ว่าจะมีสถานที่ที่ ERB อาจเหมาะสม แต่ก็มีสถานที่อื่นที่ไม่ใช่และการเพิ่มเพียงเพื่อให้โทเค็นเสีย
sockmonk

22

ถ้าฉันจำถูกต้องคุณต้องเพิ่มรหัสต่อไปนี้ในแบบฟอร์มของคุณเพื่อกำจัดปัญหานี้:

<%= token_tag(nil) %>

อย่าลืมพารามิเตอร์


8
<%= token_tag(nil) %>จริงนี้ควรจะ: จากนั้นคุณจะได้รับโทเค็นที่สร้างขึ้นอัตโนมัติ
szeryf

15

วิธีที่ง่ายที่สุดแน่นอน ไม่ต้องกังวลกับการเปลี่ยนส่วนหัว

ให้แน่ใจว่าคุณมี:

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

เพียงแค่ป้อนข้อมูลที่ซ่อนอยู่เช่น:

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

หรือถ้าคุณต้องการโพสต์ 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);
    }
});

การเพิ่มฟิลด์อินพุตที่ซ่อนอยู่ในแบบฟอร์มอินพุตของฉันแก้ไขปัญหาให้ฉันได้
Teemu Leisti

สิ่งนี้จะทำโดยอัตโนมัติหากคุณใช้ตัวช่วยสร้างแบบฟอร์มของ Rails
Jason FB

13

การอัปเกรดจากแอปรุ่นเก่าไปเป็น Rails 3.1 รวมถึงเมตาแท็ก csrf ยังคงไม่สามารถแก้ไขได้ ในบล็อก rubyonrails.org พวกเขาให้คำแนะนำในการอัพเกรดและโดยเฉพาะอย่างยิ่งบรรทัด jquery นี้ซึ่งควรจะอยู่ในส่วนหัวของโครงร่างของคุณ:

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

นำมาจากการโพสต์บล็อกนี้: http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails

ในกรณีของฉันเซสชันถูกรีเซ็ตตามคำขอ ajax แต่ละครั้ง การเพิ่มโค้ดข้างต้นแก้ไขปัญหานั้นได้


10
  1. ตรวจสอบให้แน่ใจว่าคุณมี<%= csrf_meta_tag %>ในเค้าโครงของคุณ
  2. เพิ่ม a beforeSendเพื่อรวม csrf-token ในคำร้องขอ ajax เพื่อตั้งค่าส่วนหัว สิ่งนี้จำเป็นสำหรับpostการร้องขอเท่านั้น

รหัสในการอ่าน csrf-token มีอยู่ในrails/jquery-ujsดังนั้นฉันจึงควรใช้สิ่งนั้นง่ายที่สุดดังนี้:

$.ajax({
  url: url,
  method: 'post',
  beforeSend: $.rails.CSRFProtection,
  data: {
    // ...
  }
})

ง่ายโดยไม่ต้องนำสิ่งที่มีอยู่ใน Rails มาใช้ใหม่ นี่ควรเป็นคำตอบที่เลือก
jiehanzheng

ใน Rails 5.1 ยังทำงาน:headers: { 'X-CSRF-Token': Rails.csrfToken() }
olhor

@nathanvda, บางทีคุณสามารถตอบคำถามที่คล้ายกันนี้ได้: stackoverflow.com/questions/50159847/…


6

คำตอบที่ได้รับคะแนนสูงสุดนี่ถูกต้อง แต่จะไม่ทำงานหากคุณดำเนินการตามคำขอข้ามโดเมนเนื่องจากเซสชันจะไม่สามารถใช้งานได้เว้นแต่คุณจะแจ้งให้ jQuery บอกผ่านคุกกี้เซสชันอย่างชัดเจน นี่คือวิธีการ:

$.ajax({ 
  url: url,
  type: 'POST',
  beforeSend: function(xhr) {
    xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  },
  xhrFields: {
    withCredentials: true
  }
});

ฉันขอถามคุณว่าคุณสามารถตอบคำถามที่คล้ายกันนี้ได้หรือไม่ stackoverflow.com/questions/50159847/…

5

คุณสามารถเขียนได้ทั่วโลกเช่นด้านล่าง

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

สคริปต์กาแฟ:

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

5

โอ๊ะ ..

ฉันพลาดบรรทัดต่อไปนี้ใน application.js ของฉัน

//= require jquery_ujs

ฉันแทนที่มันและมันใช้งานได้ ..

======= ปรับปรุง =========

หลังจาก 5 ปีฉันกลับมาพร้อมกับข้อผิดพลาดเดิมตอนนี้ฉันมีRailsใหม่ล่าสุด5.1.6และฉันพบโพสต์นี้อีกครั้ง เหมือนวงกลมแห่งชีวิต

ตอนนี้สิ่งที่เป็นปัญหาคือ: Rails 5.1ลบการสนับสนุนjqueryและjquery_ujsโดยค่าเริ่มต้นและเพิ่ม

//= require rails-ujs in application.js

มันทำสิ่งต่อไปนี้:

  1. กล่องโต้ตอบการยืนยันบังคับใช้สำหรับการดำเนินการต่างๆ
  2. ทำการร้องขอที่ไม่ใช่ GET จากไฮเปอร์ลิงก์
  3. ทำแบบฟอร์มหรือไฮเปอร์ลิงก์ส่งข้อมูลแบบอะซิงโครนัสกับ Ajax
  4. มีปุ่มส่งโดยอัตโนมัติในแบบฟอร์มการส่งเพื่อป้องกันการคลิกสองครั้ง (จาก: https://github.com/rails/rails-ujs/tree/master )

แต่ทำไมไม่รวมโทเค็น csrf สำหรับคำขอ ajax หากใครรู้เกี่ยวกับรายละเอียดนี้เพียงแค่แสดงความคิดเห็นฉัน ฉันขอขอบคุณที่.

อย่างไรก็ตามฉันได้เพิ่มสิ่งต่อไปนี้ในไฟล์ js ที่กำหนดเองของฉันเพื่อให้ทำงานได้ (ขอบคุณสำหรับคำตอบอื่น ๆ เพื่อช่วยให้ฉันเข้าถึงรหัสนี้):

$( document ).ready(function() {
  $.ajaxSetup({
    headers: {
      'X-CSRF-Token': Rails.csrfToken()
    }
  });
  ----
  ----
});

นี่คือสิ่งที่ฉันต้องทำเช่นกัน เหตุผลนี้เกิดขึ้นเพราะฉันสร้างแอป React ขึ้นมาเพื่อแทนที่แอพ Rails ที่มีอยู่อย่างช้าๆ เนื่องจากมีเสียงจาวาสคริปต์เกิดขึ้นในแอพที่มีอยู่ฉันจึงสร้างเลย์เอาท์ที่แตกต่างกันซึ่งเข้าถึงไฟล์จาวาสคริปต์ที่แตกต่างกัน แต่ฉันไม่สามารถรวมjquery_ujsได้ นั่นคือเคล็ดลับ
Wylliam Judd

1
ใช่บางครั้งถ้าเราพลาดเมื่อทำการทำใหม่ให้ปรับโครงสร้างบางอย่าง .. มันยากที่จะค้นหาว่ามีอะไรผิดปกติ เนื่องจากเราไม่ได้ทำอะไรด้วยตนเองเพื่อให้มันใช้งานได้ Rails จึงรวมมันโดยอัตโนมัติ เราคิดว่ามันมีอยู่แล้ว ขอบคุณสำหรับไซต์ Qn / Ans สังคมดังกล่าว
Abhi

บางทีคุณสามารถตอบคำถามที่คล้ายกันนี้: stackoverflow.com/questions/50159847/…

4

หากคุณไม่ได้ใช้ jQuery และใช้บางอย่างเช่นเรียก API สำหรับคำขอคุณสามารถใช้สิ่งต่อไปนี้เพื่อรับ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)
    })

ฉันขอถามคุณว่าคุณสามารถตอบคำถามที่คล้ายกันนี้ได้หรือไม่ stackoverflow.com/questions/50159847/…

4

ใช้ jquery.csrf ( https://github.com/swordray/jquery.csrf )

  • Rails 5.1 หรือใหม่กว่า

    $ yarn add jquery.csrf
    //= require jquery.csrf
  • Rails 5.0 หรือก่อนหน้า

    source 'https://rails-assets.org' do
      gem 'rails-assets-jquery.csrf'
    end
    //= require jquery.csrf
  • รหัสแหล่งที่มา

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


ใน 5.1 โดยใช้ webpack //= require jquery.csrfจะไม่ทำงานใช่ไหม ฉันใช้ไฟล์ pack js import 'jquery.csrf'แทน หมายเหตุคุณไม่จำเป็นต้องรวมสิ่งนี้ด้วยแท็กแพ็คในมุมมองของคุณ
ดาเมียนจัสตินŠutevski

3

หากคุณใช้ javascript กับ jQuery เพื่อสร้างโทเค็นในแบบฟอร์มของคุณการทำงานนี้:

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

เห็นได้ชัดว่าคุณต้องมี<%= csrf_meta_tag %>ในรูปแบบทับทิมของคุณ


2

สำหรับผู้ที่ต้องการคำตอบที่ไม่ใช่ jQuery คุณสามารถเพิ่มสิ่งต่อไปนี้:

xmlhttp.setRequestHeader ('X-CSRF-Token', $ ('เมตา [ชื่อ = "csrf-token"]'). attr ('เนื้อหา'));

ตัวอย่างง่ายๆสามารถเป็นเซนได้ที่นี่:

xmlhttp.open ( "โพสต์", "example.html" จริง);
xmlhttp.setRequestHeader ('X-CSRF-Token', $ ('เมตา [ชื่อ = "csrf-token"]'). attr ('เนื้อหา'));
xmlhttp.send ();

6
สิ่งนี้ไม่ใช้ jQuery สำหรับตัวเลือกหรือไม่
เทศกาลคริสต์มาส

2

ฉันต่อสู้กับปัญหานี้มาหลายวัน การเรียก GET ใด ๆ ทำงานอย่างถูกต้อง แต่ PUT ทั้งหมดจะสร้างข้อผิดพลาด "ไม่สามารถตรวจสอบความถูกต้องของโทเค็น CSRF" เว็บไซต์ของฉันทำงานได้ดีจนกระทั่งฉันได้เพิ่มใบรับรอง SSL ใน nginx

ในที่สุดฉันก็สะดุดกับสายที่ขาดหายไปในการตั้งค่า 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; 
}

หลังจากเพิ่มบรรทัดที่ขาดหายไป "proxy_set_header X-Forwarded-Proto https;" ข้อผิดพลาดโทเค็น CSRF ทั้งหมดของฉันจะปิด

หวังว่านี่จะช่วยให้คนอื่นที่กำลังตีหัวกับกำแพง ฮ่าฮ่า


1

หากมีคนต้องการความช่วยเหลือเกี่ยวกับ Uploadify และ Rails 3.2 (เช่นฉันเมื่อฉันไปโพสต์นี้) แอพตัวอย่างนี้อาจมีประโยชน์: https://github.com/n0ne/Uploadify-Carrierwave-Rails-3.2.3/blob/master /app/views/pictures/index.html.erb

ตรวจสอบโซลูชันควบคุมในแอพนี้


0

ฉันใช้ Rails 4.2.4 และหาสาเหตุไม่ได้ว่าทำไม:

Can't verify CSRF token authenticity

ฉันมีในรูปแบบ:

<%= csrf_meta_tags %>

ในตัวควบคุม:

protect_from_forgery with: :exception

การเรียกใช้tcpdump -A -s 999 -i lo port 3000แสดงส่วนหัวที่ถูกตั้งค่าไว้ (แม้ไม่ต้องการตั้งค่าส่วนหัวด้วยajaxSetup- เสร็จเรียบร้อยแล้ว):

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

ในที่สุดมันก็ล้มเหลวเพราะฉันปิดคุกกี้ CSRF ไม่ทำงานหากไม่ได้เปิดใช้งานคุกกี้ดังนั้นนี่อาจเป็นสาเหตุที่เป็นไปได้อีกประการหนึ่งหากคุณเห็นข้อผิดพลาดนี้


มันเป็นประโยชน์มาก!
joehwang
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.