CẢNH BÁO: Không thể xác minh đường ray xác thực mã thông báo CSRF


238

Tôi đang gửi dữ liệu từ chế độ xem đến bộ điều khiển bằng AJAX và tôi gặp lỗi này:

CẢNH BÁO: Không thể xác minh tính xác thực của mã thông báo CSRF

Tôi nghĩ rằng tôi phải gửi mã thông báo này với dữ liệu.

Có ai biết làm thế nào tôi có thể làm điều này?

Chỉnh sửa: Giải pháp của tôi

Tôi đã làm điều này bằng cách đặt mã sau vào trong bài AJAX:

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

7
bạn có <% = csrf_meta_tag%> trong tiêu đề bố cục của mình không?
Anatoly

có như thế này: <% = csrf_meta_tags%>
kbaccouche

6
Bạn có thư viện jquery-rails cung cấp chức năng phía máy khách ajax không?
Anatoly

2
Và cách HAML là thêm "= csrf_meta_tags"
Ege Akpinar

câu hỏi hay, cảm ơn vì đã hỏi
AMIC MING

Câu trả lời:


374

Bạn nên làm điều này:

  1. Hãy chắc chắn rằng bạn có <%= csrf_meta_tag %>trong bố trí của bạn

  2. Thêm beforeSendvào tất cả các yêu cầu ajax để đặt tiêu đề như dưới đây:


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

Để gửi mã thông báo trong tất cả các yêu cầu bạn có thể sử dụng:

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

5
Cảm ơn! Làm việc cho tôi như một nét duyên dáng!
Misha Moroshko

35
Thư viện jQuery UJS do nhóm Rails cung cấp tự động thêm mã thông báo CSRF vào yêu cầu AJAX của jQuery. README chứa các hướng dẫn về cách nhận thiết lập. github.com/rails/jquery-ujs/blob/master/src/rails.js#L91
James Conroy-Finn

1
Lưu ý rằng bạn có thể đặt tiêu đề cho tất cả các yêu cầu cùng một lúc với hàm $ .ajaxSetup.
Pieter Jongsma

1
Thông minh! Đã tìm kiếm một lúc cho câu trả lời này. Hoạt động liền mạch. Cảm ơn!
cassi.lup

4
Lưu ý, nếu bạn đang sử dụng jQuery UJS như được đề xuất ở trên, bạn cần đảm bảo rằng rails-ujs bao gồm sau jquery bao gồm hoặc nó sẽ thất bại với cùng một lỗi như op.
Topher Fangio

31

Cách tốt nhất để làm điều này thực sự chỉ là sử dụng <%= form_authenticity_token.to_s %>để in mã thông báo trực tiếp trong mã đường ray của bạn. Bạn không cần sử dụng javascript để tìm kiếm dom cho mã thông báo csrf như các bài đăng khác đề cập. chỉ cần thêm tùy chọn tiêu đề như dưới đây;

$.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
Thay vì làm điều này cho mỗi lệnh ajax, bạn có thể thêm các tiêu đề vào $ .ajaxSetup () .
Scott McMillin

1
Tôi muốn khuyên bạn nên sử dụng câu trả lời này ...
opsidao

14
Tôi không thực sự thích cách tiếp cận sử dụng ERB trong javascript.
radixhound

Điều này buộc bạn phải tạo javascript của mình bằng ERB, điều này rất hạn chế. Ngay cả khi có những nơi mà ERB có thể phù hợp, vẫn có những nơi khác không có, và thêm nó chỉ để nhận mã thông báo sẽ là một sự lãng phí.
sockmonk

22

Nếu tôi nhớ chính xác, bạn phải thêm mã sau vào biểu mẫu của mình để thoát khỏi vấn đề này:

<%= token_tag(nil) %>

Đừng quên tham số.


8
Trên thực tế, điều này nên được : <%= token_tag(nil) %>. Sau đó, bạn nhận được mã thông báo được tạo tự động.
szeryf

15

Quả thực là cách đơn giản nhất. Đừng bận tâm với việc thay đổi các tiêu đề.

Hãy chắc chắn rằng bạn có:

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

Chỉ cần làm một trường đầu vào ẩn như vậy:

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

Hoặc nếu bạn muốn một bài viết ajax của jQuery:

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

Thêm trường đầu vào ẩn vào biểu mẫu đầu vào của tôi đã giải quyết vấn đề cho tôi.
Teemu Leisti

việc này được thực hiện tự động nếu bạn sử dụng trình trợ giúp biểu mẫu của Rails.
Jason FB

13

Xấu từ một ứng dụng cũ hơn sang đường ray 3.1, bao gồm thẻ meta csrf vẫn không giải quyết được. Trên blog rubyonrails.org, họ đưa ra một số mẹo nâng cấp, và cụ thể là dòng jquery này sẽ xuất hiện trong phần đầu của bố cục của bạn:

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

lấy từ bài đăng trên blog này: http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails .

Trong trường hợp của tôi, phiên đã được đặt lại theo từng yêu cầu ajax. Thêm mã trên đã giải quyết vấn đề đó.


10
  1. Hãy chắc chắn rằng bạn có <%= csrf_meta_tag %>trong bố trí của bạn
  2. Thêm a beforeSendđể bao gồm mã thông báo csrf trong yêu cầu ajax để đặt tiêu đề. Điều này chỉ cần thiết chopost các yêu cầu.

Mã để đọc mã thông báo csrf có sẵn trong rails/jquery-ujs, vì vậy imho dễ nhất là chỉ sử dụng mã đó, như sau:

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

Đơn giản mà không cần thực hiện lại những gì đã được Rails đưa vào. Đây phải là câu trả lời được lựa chọn.
jiehanzheng

Trong Rails 5.1 cũng hoạt động:headers: { 'X-CSRF-Token': Rails.csrfToken() }
olhor 15/03/18

@nathanvda, Có lẽ bạn có thể trả lời câu hỏi tương tự này: stackoverflow.com/questions/50159847/ trên


6

Các câu trả lời được bình chọn hàng đầu ở đây là chính xác nhưng sẽ không hoạt động nếu bạn đang thực hiện các yêu cầu tên miền chéo vì phiên sẽ không có sẵn trừ khi bạn thông báo rõ ràng cho jQuery để vượt qua cookie phiên. Đây là cách để làm điều đó:

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

Tôi có thể hỏi bạn nếu bạn có thể trả lời câu hỏi tương tự này không? stackoverflow.com/questions/50159847/

5

Bạn có thể viết nó trên toàn cầu như dưới đây.

JS bình thường:

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

Cà phê Script:

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

Giáo sư..

Tôi đã bỏ lỡ dòng sau trong application.js của tôi

//= require jquery_ujs

Tôi đã thay thế nó và nó hoạt động ..

======= CẬP NHẬT =========

Sau 5 năm, tôi trở lại với cùng một lỗi, bây giờ tôi có Rails hoàn toàn mới 5.1.6 và tôi đã tìm thấy bài đăng này một lần nữa. Giống như vòng tròn của cuộc sống.

Bây giờ vấn đề là gì: Rails 5.1 đã xóa hỗ trợ cho jqueryjquery_ujs theo mặc định và đã thêm

//= require rails-ujs in application.js

Nó thực hiện những điều sau đây:

  1. buộc các hộp thoại xác nhận cho các hành động khác nhau;
  2. thực hiện các yêu cầu không NHẬN từ các siêu liên kết;
  3. tạo các biểu mẫu hoặc siêu liên kết gửi dữ liệu không đồng bộ với Ajax;
  4. có các nút gửi tự động bị vô hiệu hóa trên biểu mẫu gửi để ngăn nhấp đúp. (từ: https://github.com/rails/rails-ujs/tree/master )

Nhưng tại sao nó không bao gồm mã thông báo csrf cho yêu cầu ajax? Nếu ai biết về điều này một cách chi tiết chỉ cần bình luận cho tôi. Tôi trân trọng điều đó.

Dù sao, tôi đã thêm các mục sau vào tệp js tùy chỉnh của mình để làm cho nó hoạt động (Cảm ơn các câu trả lời khác để giúp tôi đạt được mã này):

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

Đây là những gì tôi cần phải làm là tốt. Lý do điều này xuất hiện là vì tôi đang xây dựng Ứng dụng React để từ từ thay thế Ứng dụng Rails hiện có. Vì có một loạt tiếng ồn javascript đang diễn ra trong ứng dụng hiện có, tôi đã tạo một bố cục khác để truy cập vào một tệp javascript khác, nhưng tôi đã không bao gồm jquery_ujs. Đó là mẹo.
Wylliam Judd

1
Có, đôi khi Nếu chúng ta bỏ lỡ điều đó khi làm lại, hãy cấu trúc lại một cái gì đó..Tôi rất khó để tìm ra điều gì sai. Vì chúng tôi không làm gì thủ công để làm cho nó hoạt động, Rails tự động bao gồm nó. Chúng tôi nghĩ rằng nó đã có. Cảm ơn các trang web Qn / Ans xã hội như vậy
Abhi

Có lẽ bạn có thể trả lời câu hỏi tương tự này: stackoverflow.com/questions/50159847/

4

Nếu bạn không sử dụng jQuery và sử dụng một cái gì đó như tìm nạp API cho các yêu cầu, bạn có thể sử dụng cách sau để có được 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)
    })

Tôi có thể hỏi bạn nếu bạn có thể trả lời câu hỏi tương tự này không? stackoverflow.com/questions/50159847/

4

Sử dụng jquery.csrf ( https://github.com/swordray/jquery.csrf ).

  • Rails 5.1 trở lên

    $ yarn add jquery.csrf
    //= require jquery.csrf
  • Rails 5.0 trở về trước

    source 'https://rails-assets.org' do
      gem 'rails-assets-jquery.csrf'
    end
    //= require jquery.csrf
  • Mã nguồn

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


Trong 5.1 sử dụng webpack, //= require jquery.csrfsẽ không hoạt động, phải không?. Thay vào đó tôi sử dụng gói js với import 'jquery.csrf'nó. Lưu ý rằng bạn không cần đưa cái này vào thẻ pack trong chế độ xem của bạn.
Damien Justin utevski

3

Nếu bạn đang sử dụng javascript với jQuery để tạo mã thông báo ở dạng của bạn, điều này hoạt động:

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

Rõ ràng, bạn cần phải có <%= csrf_meta_tag %>bố cục Ruby của bạn.


2

Đối với những bạn không cần câu trả lời jQuery, bạn có thể thêm vào như sau:

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

Một ví dụ rất đơn giản có thể là sen ở đây:

xmlhttp.open ("POST", "example.html", đúng);
xmlhttp.setRequestHeader ('X-CSRF-Token', $ ('meta [name = "csrf-token"]'). attr ('content'));
xmlhttp.send ();

6
Điều này không sử dụng jQuery cho bộ chọn?
Yule

2

Tôi đã vật lộn với vấn đề này trong nhiều ngày. Bất kỳ cuộc gọi GET nào đều hoạt động chính xác, nhưng tất cả các PUT sẽ tạo ra lỗi "Không thể xác minh tính xác thực mã thông báo CSRF". Trang web của tôi đã hoạt động tốt cho đến khi tôi đã thêm chứng chỉ SSL vào nginx.

Cuối cùng tôi đã vấp phải dòng bị thiếu này trong cài đặt nginx của mình:

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

Sau khi thêm dòng bị thiếu "proxy_set_header X-Forwarded-Proto https;", tất cả các lỗi mã thông báo CSRF của tôi đều thoát.

Hy vọng rằng điều này sẽ giúp người khác cũng đang đập đầu vào tường. haha



0

Tôi đang sử dụng Rails 4.2.4 và không thể hiểu tại sao tôi lại nhận được:

Can't verify CSRF token authenticity

Tôi có trong bố trí:

<%= csrf_meta_tags %>

Trong bộ điều khiển:

protect_from_forgery with: :exception

Gọi tcpdump -A -s 999 -i lo port 3000đang hiển thị tiêu đề đang được đặt (mặc dù không cần đặt tiêu đề với ajaxSetup- nó đã được thực hiện):

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

Cuối cùng thì thất bại vì tôi đã tắt cookie. CSRF không hoạt động mà không bật cookie, vì vậy đây là một nguyên nhân có thể khác nếu bạn thấy lỗi này.


nó rất hữu ích!
joehwang
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.