2016-11-11 31 views
12

在我們的Rails應用程序中,我們使用protect_from_forgery來防止CSRF。Rails protect_from_forgery如果空閒時打破登錄表單

但是,我們發現,如果用戶訪問登錄頁面,然後在應用程序會話過期時間(比如說15分鐘)的時間內喝下一杯茶。登錄只是重定向回登錄頁面,無論憑證是否成功(取決於環境,我們會得到錯誤InvalidAuthenticityToken。)嘗試再次登錄可以正常工作。它只會在用戶在頁面上的時間超過會話時間時纔會失敗。

我們認爲這很奇怪,因爲我們還沒有登錄...所以什麼會話過期?當然,即使創建並過期,也會在登錄時創建新會話。發現(看完這個後:https://nvisium.com/blog/2014/09/10/understanding-protectfromforgery/)Rails中的CSRF保護實際上使用一個會話來檢查authenticity_token是否有效。所以基本上當會話過期時(取決於session_store設置),令牌會過期,並且無需再次刷新頁面就無法登錄。

我們解決了這個問題:skip_before_action :verify_authenticity_token, only: [:create]在我們的SessionsController,但現在這意味着我們的登錄表單不再受到保護。

還有什麼其他的選擇來解決這個問題?或者,我們所用的解決方案並非如我們所想象的那樣不安全?谷歌搜索顯示這段代碼使用了很多次,但肯定這是一個壞主意?

我們其他的解決辦法是允許例外的情況發生,但是優雅地處理它:

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

雖然仍恨用戶坐在登錄頁面上比會話超時時間(在這種情況下,事實15分鐘)會導致錯誤!


另一種解決方案,我們想出了是設置session_store永遠然後手動到期的登錄會話是這樣的:

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

你使用什麼樣的'session_store'? –

回答

8

有啥會話過期?

在考慮中的會話是Rails會話(見Ruby on Rails Security GuideWhat are Sessions?),這是一種輕量級的數據存儲抽象跨HTTP請求仍然存在任意狀態(在缺省情況下,其它會話存儲實現中,加密的cookie也可以配置)。會話狀態可能包含經過身份驗證的用戶標識,但它也可用於其他目的。

在這種情況下,會話並未用於用戶身份驗證,而是將臨時「真實性令牌」存儲爲Rails安全功能的一部分,該功能可保護您的網站免受POST(或其他非Cross-Site Request Forgery(CSRF) -GET)請求。

Rails API documentation描述該功能的詳細信息:

控制器動作被包括在您的應用程序所提供的HTML的標記從跨站請求僞造(CSRF)攻擊保護。該令牌作爲會話中的隨機字符串存儲,攻擊者無權訪問該字符串。當請求到達您的應用程序時,Rails將使用會話中的令牌驗證收到的令牌。

又見Ruby on Rails Security GuideCross-Site Request Forgery節的CSRF攻擊就是一個更完整的概述,以及如何基於會話的真實性令牌驗證反對保護。

還有什麼其他的選擇來解決這個問題?

  • 加大對你的Rails會話過期時間的持續時間(例如,通過增加expire_after選項傳遞給你cookie_store初始化時間,或完全移除選項使會話永不過期)。

  • 而不是使用會話cookie有效期到期登錄會話,使用Devise:timeoutable模塊:

    devise :timeoutable, timeout_in: 15.minutes 
    

或者說是解決方案,我們已經使用並不像我們作爲不安全認爲?

配置Rails跳過verify_authenticity_token回調將禁用該特定控制器操作的CSRF保護,這使得該操作易受CSRF攻擊的影響。

所以要改一下你的問題,在任何顯著/真正意義上被禁用CSRF保護只有SessionsController#create行動仍然不安全?雖然這取決於你的應用,一般是的,我相信是這樣。

考慮這種情況:

SessionsController#create CSRF攻擊會允許攻擊者惡意直接受害者的瀏覽器登錄到攻擊者的控制下的用戶帳戶。當受害者下次訪問您的網站時(例如,當攻擊者重定向時),他們的瀏覽器仍然可以登錄到攻擊者控制的帳戶。然後,受害者可能無意中提交敏感的個人數據,或者在您的網站上執行敏感操作,以供攻擊者閱讀。

雖然這種情況可能不像其他更敏感/破壞性控制器操作禁用CSRF保護時可能發生的情況那樣危險,但它仍然暴露出足夠的潛在用戶/數據隱私問題,我認爲它是不安全的足以推薦針對禁用默認保護。

+0

非常感謝,謝謝!看起來這是一種妥協......要麼禁用令牌並打開自己的CSRF攻擊或延長/禁用會話過期,但是打開自己的其他安全問題。 – Cameron

+0

我們的一個解決方案是手動銷燬會話進行身份驗證,但允許其他會話無限期地持續。查看更新的帖子。 – Cameron

+1

@Cameron如果你延長會話cookie過期時間,你應該能夠使用Devise的'timeoutable'模塊在沒有任何附加代碼的情況下過期登錄的會話:'devise:timeoutable,timeout_in:15.minutes'。 – wjordan

1
InvalidAuthenticityToken 

當呈現表單(登錄表單)時創建的「真實性標記」已過期時出現此錯誤。

只有兩種方法可以解決此問題。

  • 配置的Rails跳過verify_authenticity_token爲控制器#行動 - 如不安全的問題已經提到
  • (只剩方式)自動重新加載屏幕,每當遇到這樣的異常(這裏登錄屏幕)。

查找相同的修改後的代碼,並在application_controller.rb內容添加到概括爲整個應用程序的解決方案。

rescue_from ActionController::InvalidAuthenticityToken do 
    redirect_to root_url 
end 
1

另一種選擇是在頁面中包含一些JS,這些JS會在令牌過期之前自動重新加載(因此保持授權令牌有效)。

如果用戶剛剛填寫表單,您可能需要在重新加載之前檢查鼠標/鍵盤動作。

這個選項的最大缺點似乎是,JS與auth令牌有效的實際時間量是分開的......所以如果你改變你的超時時間,這可能會是一個維護問題通常持續時間