Rails: Không thể xác minh tính xác thực của mã thông báo CSRF khi thực hiện yêu cầu ĐĂNG


90

Tôi muốn gửi cho nhà POST requestphát triển địa phương của mình, như thế này:

  HTTParty.post('http://localhost:3000/fetch_heroku',
                :body => {:type => 'product'},)

Tuy nhiên, từ bảng điều khiển máy chủ, nó báo cáo

Started POST "/fetch_heroku" for 127.0.0.1 at 2016-02-03 23:33:39 +0800
  ActiveRecord::SchemaMigration Load (0.0ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by AdminController#fetch_heroku as */*
  Parameters: {"type"=>"product"}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 1ms

Đây là bộ điều khiển và thiết lập các tuyến đường của tôi, nó khá đơn giản.

  def fetch_heroku
    if params[:type] == 'product'
      flash[:alert] = 'Fetch Product From Heroku'
      Heroku.get_product
    end
  end

  post 'fetch_heroku' => 'admin#fetch_heroku'

Tôi không chắc mình cần phải làm gì? Để tắt CSRF chắc chắn sẽ hoạt động, nhưng tôi nghĩ rằng đó là sai lầm của tôi khi tạo một API như vậy.

Có bất kỳ thiết lập nào khác mà tôi cần thực hiện không?


5
Đối với các API thường được chấp nhận để tắt xác thực mã thông báo CSRF . Tôi sử dụng protect_from_forgery with: :null_session.
dcestari

Câu trả lời:


110

Giả mạo yêu cầu trên nhiều trang web (CSRF / XSRF) là khi một trang web độc hại lừa người dùng thực hiện một yêu cầu không nhằm mục đích, chẳng hạn bằng cách sử dụng bookmarklet, iframe hoặc chỉ bằng cách tạo một trang đủ giống về mặt hình ảnh để đánh lừa người dùng.

Các bảo vệ Rails CSRF được thực hiện cho các ứng dụng web "cổ điển" - nó chỉ đơn giản đưa ra một mức độ đảm bảo rằng yêu cầu có nguồn gốc từ ứng dụng web của riêng bạn. Mã thông báo CSRF hoạt động giống như một bí mật mà chỉ máy chủ của bạn biết - Rails tạo mã thông báo ngẫu nhiên và lưu trữ nó trong phiên. Biểu mẫu của bạn gửi mã thông báo qua một đầu vào ẩn và Rails xác minh rằng bất kỳ yêu cầu nào không phải GET đều bao gồm mã thông báo khớp với những gì được lưu trữ trong phiên.

Tuy nhiên, theo định nghĩa, một API thường là trang web chéo và được sử dụng trong nhiều ứng dụng web của bạn, có nghĩa là toàn bộ khái niệm về CSRF không hoàn toàn áp dụng.

Thay vào đó, bạn nên sử dụng chiến lược xác thực yêu cầu API dựa trên mã thông báo bằng khóa API và bí mật vì bạn đang xác minh rằng yêu cầu đến từ ứng dụng khách API được phê duyệt - không phải từ ứng dụng của riêng bạn.

Bạn có thể hủy kích hoạt CSRF như được chỉ ra bởi @dcestari:

class ApiController < ActionController::Base
  protect_from_forgery with: :null_session
end

Đã cập nhật. Trong Rails 5, bạn có thể tạo các ứng dụng chỉ API bằng cách sử dụng --apitùy chọn:

rails new appname --api

Chúng không bao gồm phần mềm trung gian CSRF và nhiều thành phần khác là chất siêu lỏng.


Cảm ơn, tôi chọn tắt một phần CSRF: stackoverflow.com/questions/5669322/…
cqcn1991

4
điều này cho thấy rằng tất cả các API đều phân phát lưu lượng ứng dụng đến ứng dụng (trong đó một đối tác duy nhất được cấp một khóa & bí mật duy nhất). Trong trường hợp đó, giống như giao tiếp từ máy chủ đến máy chủ, câu trả lời này là phù hợp. TUY NHIÊN, điều khó hiểu đối với hầu hết các nhà phát triển ứng dụng web là đối với một ứng dụng khách Javascript, do bạn kiểm soát và viết, bạn KHÔNG MUỐN sử dụng một khóa bí mật duy nhất (điều đó sẽ tiết lộ một khóa bí mật duy nhất cho tất cả các ứng dụng). Thay vào đó, CSRF và cơ chế phiên cookie của Rail hoạt động hiệu quả - ngay cả đối với các ứng dụng Javascript sử dụng API của bạn - nếu bạn chuyển lại mã thông báo CSRF cho Rails với mỗi yêu cầu.
Jason FB

cách bạn thực hiện việc này trong AJAX được mô tả trong bài đăng SO này stackoverflow.com/questions/7203304/…
Jason FB

@JasonFB bạn nói đúng ở chỗ một bí mật duy nhất với các ứng dụng khách javascript không hoạt động. Tuy nhiên, việc sử dụng các phiên và bảo vệ Rails CSRF vẫn có vấn đề nếu bạn định xây dựng một API cũng có thể được sử dụng bởi các loại ứng dụng khách không phải là trình duyệt.
tối đa

Nếu bạn cung cấp hoặc xây dựng một giải pháp thay thế cho CSRF, hãy là khách của tôi. Chỉ cần biết lý do cho những gì bạn đang thay thế để bạn biết những gì thích hợp để thay thế nó. Rõ ràng, bây giờ phân minh của chúng ta trở thành ngữ cảnh.
Jason FB

78

Một cách khác để tắt CSRF sẽ không hiển thị phiên rỗng là thêm:

skip_before_action :verify_authenticity_token

trong Bộ điều khiển Rails của bạn. Điều này sẽ đảm bảo bạn vẫn có quyền truy cập vào thông tin phiên.

Một lần nữa, hãy đảm bảo rằng bạn chỉ thực hiện việc này trong bộ điều khiển API hoặc ở những nơi khác mà bảo vệ CSRF không được áp dụng.


1
Điều này giống hơn protect_from_forgery except: [:my_method_name]?
Arnold Roa

19

Có thông tin liên quan về cấu hình CSRF liên quan đến bộ điều khiển API trên api.rubyonrails.org :

Điều quan trọng cần nhớ là các yêu cầu XML hoặc JSON cũng bị ảnh hưởng và nếu bạn đang xây dựng một API, bạn nên thay đổi phương pháp bảo vệ giả mạo trong ApplicationController(theo mặc định :exception:):

class ApplicationController < ActionController::Base
  protect_from_forgery unless: -> { request.format.json? }
end

Chúng tôi có thể muốn tắt tính năng bảo vệ CSRF cho các API vì chúng thường được thiết kế để không có trạng thái. Nghĩa là, ứng dụng API yêu cầu sẽ xử lý phiên cho bạn thay vì Rails.


4
Thật là khó hiểu. Tôi đang phân vân giữa các nguồn nói rằng điều đó protect_from_forgeryvẫn cần thiết cho API và các nguồn nói rằng nó không. Điều gì sẽ xảy ra nếu API dành cho một ứng dụng trang duy nhất, sử dụng cookie phiên để xác thực người dùng?
Wylliam Judd

Cơ chế CSRF là cách tích hợp của Rails để đối phó với vectơ tấn công bằng cách sử dụng cookie phiên. Cơ chế này bảo vệ bộ điều khiển của bạn, trong khi hoạt động cùng với (thực sự bên trong) của cookie phiên Rails. Nếu không có protect_from_forgery hoặc tắt hoặc đặt ngoại trừ trên đó, bạn đang nói với Rails KHÔNG bảo vệ hành động đó bằng cách sử dụng thông tin từ mã thông báo CSRF (được lấy từ cookie phiên).
Jason FB

5
Tôi nghĩ điều khó hiểu là đối với tài liệu Rails, "API" có nghĩa là ứng dụng từ máy chủ đến máy chủ sẽ nhận các yêu cầu API từ các đối tác từ xa, đáng tin cậy (không phải tất cả người dùng web truy cập trang web của bạn). Đối với những ppl đó, hãy cung cấp các cặp khóa-bí mật duy nhất thông qua cơ chế an toàn không thể hack. Đối với các ứng dụng web hiện đại, nơi nhiều người sẽ tải ứng dụng web của bạn thông qua ứng dụng khách web, bạn vẫn sử dụng cơ chế dựa trên phiên hoặc mã thông báo để xác định duy nhất từng người truy cập trang web của bạn. Vì vậy, trừ khi bạn đang sử dụng MỘT SỐ cơ chế KHÁC để thực hiện việc này (mã thông báo Json Web, v.v.), hãy gắn bó với những thứ có sẵn của Rails.
Jason FB

1
cách bạn thực hiện việc này trong AJAX được mô tả trong bài đăng SO này stackoverflow.com/questions/7203304/…
Jason FB

13

Vì Rails 5, bạn cũng có thể tạo một lớp mới với :: API thay vì :: Base:

class ApiController < ActionController::API
end

3

Nếu bạn muốn loại trừ hành động mẫu của bộ điều khiển mẫu

class TestController < ApplicationController
  protect_from_forgery :except => [:sample]

  def sample
     render json: @hogehoge
  end
end

Bạn có thể xử lý các yêu cầu từ bên ngoài mà không gặp bất kỳ vấn đề gì.


0

Giải pháp đơn giản nhất cho vấn đề là làm những thứ tiêu chuẩn trong bộ điều khiển của bạn hoặc bạn có thể đặt trực tiếp nó vào ApplicationController

class ApplicationController < ActionController::Base protect_from_forgery with: :exception, prepend: true end

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.