rails - "CẢNH BÁO: Không thể xác minh tính xác thực của mã thông báo CSRF" cho các yêu cầu json devise


82

Làm cách nào để truy xuất mã thông báo CSRF để chuyển với một yêu cầu JSON?

Tôi biết rằng vì lý do bảo mật, Rails đang kiểm tra mã thông báo CSRF trên tất cả các loại yêu cầu (bao gồm cả JSON / XML).

Tôi có thể đặt bộ điều khiển của mình skip_before_filter :verify_authenticity_token, nhưng tôi sẽ mất bảo vệ CRSF (không khuyến khích :-)).

Câu trả lời tương tự (vẫn không được chấp nhận) này gợi ý cho

Lấy mã thông báo bằng <%= form_authenticity_token %>

Câu hỏi là làm thế nào? Tôi có cần thực hiện cuộc gọi đầu tiên đến bất kỳ trang nào của mình để truy xuất mã thông báo và sau đó thực hiện xác thực thực của tôi với Devise không? Hay đó là thông tin một lần mà tôi có thể nhận được từ máy chủ của mình và sau đó sử dụng nhất quán (cho đến khi tôi tự thay đổi thông tin trên chính máy chủ)?

Câu trả lời:


127

CHỈNH SỬA :

Trong Rails 4, bây giờ tôi sử dụng những gì @genkilabs đề xuất trong nhận xét bên dưới:

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }

Điều này, thay vì tắt hoàn toàn tính năng bảo mật được tích hợp sẵn, hãy loại bỏ bất kỳ phiên nào có thể tồn tại khi có thứ gì đó truy cập vào máy chủ mà không có mã thông báo CSRF.


skip_before_filter :verify_authenticity_token, :if => Proc.new { |c| c.request.format == 'application/json' }

Điều này sẽ tắt kiểm tra CSRF cho các bài đăng / bài viết json đã được đánh dấu đúng cách như vậy.

Ví dụ: trong iOS, cài đặt nội dung sau cho NSURLRequest của bạn, trong đó "tham số" là tham số của bạn:


[request setHTTPMethod:@"POST"];

[request setValue:@"application/json" 
       forHTTPHeaderField:@"content-type"];

[request setValue:@"application/json" 
       forHTTPHeaderField:@"accept"];

[request setHTTPBody:[NSData dataWithBytes:[parameters UTF8String] 
                                            length:[parameters length]]];

3
làm điều này, bạn sẽ bị tấn công nếu kẻ tấn công đang đánh dấu định dạng là 'json'. Và đó là lý do tại sao Rails theo định nghĩa đang đưa ra một cảnh báo. Tôi muốn có json của mình để có thể chuyển đúng một mã thông báo, nếu không, có cảnh báo trong nhật ký là tốt.

21
bạn có thể bị tấn công, đó là lý do tại sao họ khuyên bạn nên có một số biện pháp bảo mật khác khi tắt bộ lọc. Thông thường đối với các yêu cầu API, người yêu cầu sẽ gửi một khóa API cùng với dữ liệu bài đăng, sau đó bạn sẽ xác minh trước khi thực thi phương thức mong muốn.
Ryan Crews

18
Trong đường ray 4 bạn có thể làm như sau:protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/json' }
genkilabs

2
@genkilabs Tôi thích điều đó, đã thêm để trả lời cho người dùng rails 4, cảm ơn =]
Ryan Crews

1
Hai điều tôi đã làm ngay bây giờ để làm cho điều này hoạt động trong ứng dụng Rails & iOS của tôi: 1) Tôi không thấy lý do gì tại sao chúng tôi lại trộn cú pháp băm kiểu cũ và kiểu mới trong Ruby, vì vậy mã trong bộ điều khiển của tôi trông như sau: protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }2) Không liên quan đến câu hỏi này nhưng liên quan đến chủ đề ĐĂNG JSON từ iOS, vì tôi đang sử dụng AFNetworkingvà Rails không tự động gói các thông số, tôi đã làm như sau:manager.requestSerializer = [AFJSONRequestSerializer serializer];
mharper

18

Bạn có thể gửi mã thông báo CSRF, sau khi đăng nhập thành công, bằng cách sử dụng tiêu đề tùy chỉnh.

Ví dụ: đặt cái này vào các phiên của bạn # create:

response.headers['X-CSRF-Token'] = form_authenticity_token

Tiêu đề phản hồi đăng nhập mẫu cung cấp mã thông báo CSRF:

HTTP/1.1 200 OK
Cache-Control: max-age=0, private, must-revalidate
Connection: Keep-Alive
Content-Length: 35
Content-Type: application/json; charset=utf-8
Date: Mon, 22 Oct 2012 11:39:04 GMT
Etag: "9d719d3b9aabd413c3603e04e8a3933d"
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-10-12)
Set-Cookie: [cut for readability] 
X-Csrf-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=
X-Request-Id: 178746992d7aca928c876818fcdd4c96
X-Runtime: 0.169792
X-Ua-Compatible: IE=Edge

Mã thông báo này có giá trị cho đến khi bạn đăng nhập lại hoặc (đăng xuất nếu bạn hỗ trợ mã này thông qua API của mình). Khách hàng của bạn có thể trích xuất và lưu trữ mã thông báo từ các tiêu đề phản hồi đăng nhập. Sau đó, mỗi yêu cầu POST / PUT / DELETE phải đặt tiêu đề X-CSRF-Token với giá trị nhận được tại thời điểm đăng nhập.

Tiêu đề POST mẫu với mã thông báo CSRF:

POST /api/report HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate, compress
Content-Type: application/json; charset=utf-8
Cookie: [cut for readability]
Host: localhost:3000
User-Agent: HTTPie/0.3.0
X-CSRF-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=

Tài liệu: form_authenticity_token


18

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 đề.

Đảm bảo rằng bạn có:

<%= csrf_meta_tag %>

trong của bạn layouts/application.html.erb

Chỉ cần thực hiện một trường nhập ẩn như sau:

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

Hoặc nếu bạn muốn có một bài đăng ajax 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);
    }
});

Về cơ bản khi bạn đăng dữ liệu json của mình, chỉ cần thêm trường authenticity_token hợp lệ vào postdữ liệu và cảnh báo sẽ biến mất ...


IMHO nó không nên chỉ đặt một cảnh báo trong logger nhưng bài viết hoàn toàn loại bỏ mà không có một thẻ CSRF hợp lệ theo mặc định nó chỉ bài viết cảnh báo và nộp dữ liệu tốt ...
Walter Schreppers

Cách ngắn hơn để thực hiện trường nhập mã thông báo xác thực ẩn là = token_tag(nil)(không quan trọng)
Yo Ludke

Trong các phiên bản cũ hơn của rails, thẻ kết thúc bằng số nhiều. <% = csrf_meta_tags%>
cyonder

4

Tôi đã giải quyết lỗi đó theo cách này:

class ApplicationController < ActionController::Base
  protect_from_forgery
  skip_before_action :verify_authenticity_token, if: :json_request?

  protected

  def json_request?
    request.format.json?
  end
end

Nguồn: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html


Tôi không chắc bạn muốn làm điều này - các trình duyệt có thể sử dụng các API JSON.
Joe Van Dyk

Cảm ơn bạn! Cần một cách nhanh chóng (và bẩn) để giải quyết vấn đề này và điều này có thể hoạt động đủ tốt cho những gì tôi đang cố gắng đạt được.
NerdyTherapist

Tài liệu được liên kết (hiện được cập nhật cho Rails 5) dường như là: protect_from_forgery with: :exception, unless: -> { request.format.json? }và nó hoạt động với tôi. Cảm ơn.
akostadinov

1
Điều này chỉ vô hiệu hóa kiểm tra, tránh sự cố. Nếu và ứng dụng sẽ vô hiệu hóa điều này thì việc bật tính năng bảo vệ khỏi giả mạo là gì?
jefflunt

3

Điều đáng lo ngại là trong Rails 3.2.3 bây giờ chúng ta nhận được cảnh báo CSRF trong production.log nhưng bài đăng không bị lỗi! Tôi muốn nó không thành công vì nó bảo vệ tôi khỏi các cuộc tấn công. Và bạn có thể thêm mã thông báo csrf bằng jquery trước bộ lọc btw:

http://jasoncodes.com/posts/rails-csrf-vulnerability


2

Tôi đã sử dụng bên dưới. Sử dụng bao gồm? vì vậy nếu loại nội dung là application / json; charset = utf-8 thì nó vẫn hoạt động.

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format.include? 'application/json' }

2

Câu trả lời này là tốt hơn.

Bạn có thể duy trì xác thực CSRF-TOKEN mà không cần nỗ lực thêm (mã thông báo được nối thêm) trước khi gửi bất kỳ XMLHttpRequest nào. Không có JQuery, không có gì chỉ sao chép / dán và làm mới.

Chỉ cần thêm mã này.

(function() {
    var send = XMLHttpRequest.prototype.send,
        token = $('meta[name=csrf-token]').attr('content');
    XMLHttpRequest.prototype.send = function(data) {
        this.setRequestHeader('X-CSRF-Token', token);
        return send.apply(this, arguments);
    };
}());

1

Tôi gặp sự cố tương tự với phiên bản sau của Rails:
gem 'rails',: git => 'git: //github.com/rails/rails.git',: branch => '3-2-ổn định'

Tôi đã cập nhật lên 3.2.2 và mọi thứ hoạt động tốt với tôi bây giờ. :)
đá quý 'rails', '3.2.2'


cám ơn vì sự gợi ý. Tôi đã thử nó ra, nhưng tôi vẫn có cảnh báo tương tựWARNING: Can't verify CSRF token authenticity

0

Tôi đã gặp phải vấn đề tương tự tối nay. Lý do điều đó xảy ra là vì khi bạn đăng nhập csrf-token cuối cùng không còn hợp lệ. Những gì tôi đã làm là: $("meta[name=csrf-token]").attr('content', '<%= form_authenticity_token %>');trong ứng dụng / views / devise / session / create.js.rb của bạn.

Bây giờ nó có một mã thông báo csrf hợp lệ :) Tôi hy vọng nó sẽ giúp


0

Cũng cho chế độ phát triển / thử nghiệm.

protect_from_forgery with: :exception unless %w(development test).include? Rails.env

Cảnh báo này hiển thị vì bạn đang sử dụng :null_session, trong Rails 4.1, nó hoạt động theo mặc định nếu không có with:tùy chọn nào được chỉ định.

protect_from_forgery
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.