2016-11-11 11 views
12

Trong ứng dụng Rails của chúng tôi, chúng tôi sử dụng protect_from_forgery để ngăn chặn CSRF. Tuy nhiên, những gì chúng tôi thấy là nếu người dùng truy cập trang đăng nhập và sau đó tắt để pha một tách trà trong khoảng thời gian hết hạn phiên ứng dụng (giả sử 15 phút). Đăng nhập chỉ chuyển hướng trở lại trang đăng nhập bất kể thông tin đăng nhập thành công hay không (Tùy thuộc vào môi trường, chúng tôi gặp lỗi InvalidAuthenticityToken.) Cố gắng đăng nhập lại hoạt động tốt. Nó chỉ thất bại nếu người dùng đã ở trên trang lâu hơn thời gian phiên.Đường ray protect_from_forgery phá vỡ biểu mẫu đăng nhập nếu không hoạt động

Chúng tôi nghĩ điều này thật kỳ lạ vì chúng tôi chưa đăng nhập ... vì vậy phiên nào sắp hết hạn? và chắc chắn một phiên mới đang được tạo khi đăng nhập ngay cả khi phiên đã được tạo và đã hết hạn. Hóa ra (sau khi đọc điều này: https://nvisium.com/blog/2014/09/10/understanding-protectfromforgery/) bảo vệ CSRF trong Rails thực sự sử dụng một phiên để kiểm tra authenticity_token là hợp lệ. Vì vậy, về cơ bản mã thông báo sẽ hết hạn khi phiên hết hạn (tùy thuộc vào cài đặt session_store) và bạn không thể đăng nhập mà không làm mới trang một lần nữa.

Chúng tôi đã giải quyết vấn đề này: skip_before_action :verify_authenticity_token, only: [:create] trong số SessionsController nhưng bây giờ điều đó có nghĩa là biểu mẫu đăng nhập của chúng tôi không còn được bảo vệ.

Có các tùy chọn nào khác để khắc phục sự cố này? Hay là giải pháp chúng ta đã sử dụng không an toàn như chúng ta nghĩ? Googling cho thấy dòng mã này được sử dụng rất nhiều lần, nhưng chắc chắn đó là một ý tưởng tồi?

giải pháp khác của chúng tôi là để cho phép các ngoại lệ xảy ra, nhưng xử lý nó một cách duyên dáng với:

rescue_from ActionController::InvalidAuthenticityToken do 
    @exception = exception.message 
    render 'errors/500', :status => 500, :layout => 'other' 
end 

Mặc dù vẫn ghét thực tế người dùng ngồi trên trang đăng nhập lâu hơn thời gian chờ phiên (trong trường hợp này 15 phút) gây ra lỗi!


Tuy nhiên, một giải pháp chúng tôi đã đưa ra là để thiết lập session_store để mãi mãi và sau đó tự hết hạn phiên đăng nhập như thế này:

before_action :session_timeout, if: :current_user 

def session_timeout 
    session[:last_seen_at] ||= Time.now 
    if session[:last_seen_at] < 15.minutes.ago 
    reset_session 
    else 
    session[:last_seen_at] = Time.now 
    end 
end 
+0

Bạn sử dụng loại 'session_store' nào? –

Trả lời

8

vì vậy những gì phiên hết hạn?

Phiên họp trong câu hỏi là Rails phiên (xem What are Sessions? trong Ruby on Rails Security Guide), mà là một sự trừu tượng dữ liệu lưu trữ nhẹ mà vẫn tồn tại tình trạng tuỳ tiện trên các yêu cầu HTTP (trong một cookie được mã hóa theo mặc định, triển khai phiên lưu trữ khác cũng có thể được cấu hình). Trạng thái phiên có thể bao gồm ID người dùng được xác thực, nhưng nó cũng có thể được sử dụng cho các mục đích khác.

Trong trường hợp này, phiên không được sử dụng để xác thực người dùng, nhưng để lưu trữ 'mã thông báo xác thực' tạm thời như một phần của tính năng bảo mật Rails bảo vệ trang web của bạn chống lại các cuộc tấn công POST (hoặc khác không phải là Cross-Site Request Forgery (CSRF) -GET) yêu cầu.

Các Rails API documentation mô tả tính năng này một cách chi tiết hơn: hành động

điều khiển được bảo vệ khỏi Cross-Site Request giả mạo (CSRF) tấn công bằng cách bao gồm một thẻ trong HTML rendered cho ứng dụng của bạn. Mã thông báo này được lưu trữ dưới dạng chuỗi ngẫu nhiên trong phiên mà kẻ tấn công không có quyền truy cập. Khi một yêu cầu đến ứng dụng của bạn, Rails sẽ kiểm tra mã nhận được với mã thông báo trong phiên.

cũng Xem phần Cross-Site Request Forgery của Ruby on Rails Security Guide cho một cái nhìn tổng quan đầy đủ hơn về những gì một cuộc tấn công CSRF là, và làm thế nào xác minh tính xác thực-thẻ dựa trên phiên bảo vệ chống lại nó.

Còn các tùy chọn khác để khắc phục sự cố này?

  • Tăng thời hạn của thời gian hết hạn phiên Rails của bạn (ví dụ, bằng cách tăng thời gian tùy chọn expire_after truyền cho cookie_store initializer của bạn, hoặc loại bỏ các tùy chọn hoàn toàn để làm cho phiên bao giờ hết hạn).

  • Thay vì sử dụng phiên cookie hết hạn hết hạn phiên đăng nhập, sử dụng Devise 's :timeoutable mô-đun:

    devise :timeoutable, timeout_in: 15.minutes 
    

Hoặc là giải pháp chúng tôi đã sử dụng không phải là không an toàn như chúng ta nghĩ sao?

Định cấu hình Rails để bỏ qua verify_authenticity_token gọi lại vô hiệu hóa bảo vệ CSRF cho hành động điều khiển cụ thể đó khiến hành động dễ bị tấn công CSRF.

Vì vậy, để nói lại câu hỏi của bạn, được vô hiệu hóa CSRF bảo vệ chỉcho hành động SessionsController#create vẫn không an toàn trong bất kỳ ý nghĩa quan trọng/có ý nghĩa? Mặc dù điều này phụ thuộc vào đơn đăng ký của bạn, nói chung là , tôi tin như vậy.

Hãy xem xét kịch bản này:

Một CSRF tấn công chống lại SessionsController#create sẽ cho phép kẻ tấn công cố đạo trình duyệt của nạn nhân để đăng nhập vào một tài khoản người dùng dưới sự kiểm soát của kẻ tấn công. Lần tiếp theo nạn nhân truy cập vào trang web của bạn (ví dụ: khi được kẻ tấn công chuyển hướng), trình duyệt của họ vẫn có thể đăng nhập vào tài khoản do kẻ tấn công kiểm soát. Các nạn nhân sau đó có thể vô tình gửi dữ liệu cá nhân nhạy cảm hoặc thực hiện các hành động nhạy cảm trên trang web của bạn có thể được đọc bởi kẻ tấn công.

Trong trường hợp này có thể ít rõ ràng nguy hiểm hơn những gì có thể xảy ra nếu bảo vệ CSRF bị vô hiệu hóa trên các hành động điều khiển nhạy cảm/phá hoại khác, nó vẫn phơi bày đủ vấn đề về quyền riêng tư của người dùng/dữ liệu mà tôi cho là không an toàn đủ để đề xuất chống lại tắt tính năng bảo vệ mặc định.

+0

Phản hồi tuyệt vời, cảm ơn! Có vẻ như đó là một chút thỏa hiệp ... hoặc vô hiệu hóa mã thông báo và mở chính bạn cho cuộc tấn công CSRF hoặc mở rộng/vô hiệu hóa hết hạn phiên nhưng sau đó tự mở cho mình các vấn đề bảo mật khác. – Cameron

+0

Một giải pháp mà chúng tôi đã có là tự hủy các phiên cho auth nhưng cho phép các phiên khác kéo dài vô thời hạn. Xem bài đăng cập nhật. – Cameron

+1

@Cameron nếu bạn mở rộng hết hạn cookie phiên, bạn sẽ có thể sử dụng mô-đun 'timeoutable' của Devise để hết hạn các phiên đăng nhập mà không cần thêm bất kỳ mã nào:' devise: timeoutable, timeout_in: 15.minutes'. – wjordan

1
InvalidAuthenticityToken 

Lỗi này xảy ra khi "mã thông báo xác thực" được tạo trong khi biểu mẫu hiển thị (biểu mẫu đăng nhập) đã hết hạn.

Chỉ có hai cách để chúng tôi có thể giải quyết vấn đề này.

  • Cấu hình Rails bỏ qua verify_authenticity_token cho # hành động điều khiển - không an toàn như đã đề cập trong câu hỏi
  • (Chỉ cách trái) Auto reload màn hình (ở đây Đăng nhập màn hình) bất cứ khi nào ngoại lệ như vậy gặp phải.

Tìm mã được sửa đổi cho cùng và thêm mã này vào application_controller.rb để khái quát hóa giải pháp cho toàn bộ ứng dụng.

rescue_from ActionController::InvalidAuthenticityToken do 
    redirect_to root_url 
end 
1

Một lựa chọn khác sẽ bao gồm một số JS trên trang đó sẽ tự động tải lại một thời gian ngắn trước khi thẻ của bạn hết hạn (và do đó giữ token auth hợp lệ).

Bạn có thể muốn kiểm tra để kiểm tra hành động chuột/bàn phím trước khi tải lại trong trường hợp người dùng chỉ đang trong quá trình điền biểu mẫu. Có vẻ như nhược điểm lớn nhất của tùy chọn này là thực tế rằng JS được tách ra khỏi khoảng thời gian thực tế mã thông báo xác thực hợp lệ ... Vì vậy, nó có thể chứng minh là vấn đề bảo trì nếu bạn thay đổi thời gian chờ của mình thời gian thường xuyên.

Các vấn đề liên quan