2013-05-12 76 views
12

對於我的應用程序,我正在實施與zentask中顯示的相同的安全性。發揮關於cookie和會話的框架安全問題

public class Secured extends Authenticator { 

@Override 
public String getUsername(Context ctx) { 
    return ctx.session().get("email"); 

} 

@Override 
public Result onUnauthorized(Context ctx) { 
    ctx.flash().put("error", "please login to proceed"); 
    return redirect(routes.Application.index()); 
} 

} 

當用戶通過驗證isuser session().put("email", email);

我有兩個問題。首先:當用戶離開應用程序而不使用註銷時,您如何使會話失效?其次更嚴重的是我使用firefox插件cookies manager+檢查了cookie,我可以複製一個cookie並稍後粘貼它,因此我可以訪問方法而不必先登錄,基本上我可以竊取會話

回答

36

播放框架使用無狀態的會話。在服務器端沒有存儲狀態,相反,所有狀態都存儲在會話cookie中。要驗證會話,Play使用密鑰簽名會話,並在帶有會話cookie的請求到達時驗證簽名。如果用戶篡改會話數據,例如,如果他們將會話中的電子郵件地址更改爲其他人的電子郵件地址,則簽名不匹配,因此Play會拒絕會話Cookie。

是的,你可以複製的cookie,並在以後使用。但是你不能更改cookie。這意味着您可以「竊取」的唯一cookie是您自己的,但是從自己竊取並不是真正的竊取行爲。所以不行,除了通過利用其他漏洞(如XSS)之外,您不能竊取會話,但會話令牌也容易受到攻擊。

默認情況下,Play配置爲創建「會話」Cookie,即在關閉瀏覽器時過期的Cookie。因此,如果用戶退出瀏覽器,瀏覽器將刪除所有會話cookie,並且用戶將不再登錄。會話令牌也是如此。

需要注意的一點是,會話令牌也會在服務器上過期,因爲服務器會保持狀態。無狀態簽名的會話(如Play中使用的會話)不會。但是,您可以通過在創建時在會話中存儲時間戳並在getUsername()方法中驗證該時間戳不晚於配置的有效期來實現過期機制。由於時間戳存儲在已簽名的會話中,因此不改變簽名就不會篡改時間戳,因此這種簡單的機制非常安全。更高級的解決方案可能是實現一個篩選器,每次請求進入時都會更新該時間戳,以便到期時間可以基於上次訪問時間,而不是基於用戶登錄時間。

+1

「,但會話令牌也容易受此影響。」在普通服務器上,當我註銷時,任何盜取我的cookie的人都不能再使用它了。是的,曲奇不應該被盜,但需要深度防守。 – 2015-03-07 02:36:03

+3

這個。應該實施到Play中。 – BAR 2015-08-23 01:39:32

4

簡單地將用戶ID放入一個cookie根本就不是安全的。正如你指出的,任何人都可以發明一個cookie值。相反,您需要在Cookie中放入一個任意值(例如隨機),然後在服務器上查找映射表中用戶的身份。這個任意值必須經常改變,所以你通常會有一個持續30分鐘的登錄會話。每個登錄提供一個新的任意值,並且該值被稱爲會話ID。

失效:在一段時間沒有任何請求(例如30分鐘)後,通過從查找表中刪除該條目(在服務器端),會話失效。任何不在表中的會話ID的請求都會被視爲未經身份驗證的請求,並且您會再次提示登錄。用戶忘記註銷並不重要。

黑客行爲:由於該值是任意的,因此黑客無法事先知道未來的會話ID是什麼。你仍然很容易受到會話竊取的影響,但它更加困難:黑客必須僅在使用時找到會話ID,然後才能在特定時間內使用它。您可以採取一些措施來防止這種情況發生,例如只允許來自特定IP地址的特定會話請求。即使是每個請求,您也可以快速循環會話ID,但是有負面的一面。一般來說,爲每次登錄提供一個唯一的會話ID(特別是通過HTTPS完成時),對於大多數身份驗證需求來說已足夠好。

持久性:如果併發用戶的數量超過任何給定的會話期間(例如30分鐘)較小,則不一定需要把這個數據庫中。將其維持在內存中的開銷較低,但具有的缺點是,如果循環使用服務器,則所有用戶都需要重新登錄。如果確實將會話ID放入數據庫中,則需要確保服務器啓動時可清除所有舊會話。

用戶信息:將用戶的電子郵件地址放入cookie中仍有價值,但僅作爲「默認」用戶標識登錄爲。這應該只是爲了方便用戶,而不是作爲用戶認證的指示。

+0

ive將以下信息放在cookie上session()。put(「email」,email); ()「(timestamp」,new Date()。toString()); ()。put(「ip」,request()。remoteAddress());並比較它。這夠好嗎? – naoru 2013-05-12 16:54:41

+0

取決於你是否希望黑客進入或不進入。任何人都可以生成電子郵件,時間戳和IP地址,如果這是您需要進行身份驗證的全部內容,那麼您將被黑客入侵。密鑰是一個隨機生成的號碼,將服務器映射到經過身份驗證的用戶(和IP地址)。如果沒有真正的身份驗證,黑客無法在您的(服務器端)表中獲得條目。 – AgilePro 2013-05-17 05:20:51

+0

這樣生成和UUID,將它分配給cookie並且在說30分鐘之後使該UUID失效會帶來相對較好的保護? – naoru 2013-05-17 13:22:33

7

您的假設是絕對正確的,您無法使Zentask示例中的服務器上的會話無效。雖然會話cookie是使用配置文件中的私鑰簽名的,但同一個未簽名的cookie值會生成已簽名的相同cookie。正如你已經想到的那樣,如果有人從用戶那裏竊取了cookie,用戶和你(服務器)都不能阻止小偷「登錄」到用戶的賬戶中。

基本上有現在兩個選項:

  1. 儲存在你和用戶知道的cookie揮發性信息。一個例子就是用戶的密碼哈希。如果用戶更改此信息,則舊Cookie將失效。但我不會推薦使用密碼哈希,因爲它是一個有價值的信息。壞消息是:如果用戶沒有更改這些信息,那麼Cookie將會在很長一段時間內有效。
  2. 進行服務器端會話管理。爲此,你必須有一個像數據庫的東西。在那裏存儲會話的隨機生成密鑰,用戶和會話自動失效的日期。您還可以存儲IP地址,以提高防止cookie竊取的安全性。會話密鑰必須寫入cookie中。當用戶點擊註銷按鈕時,會使當前會話(或者此用戶的所有會話)無效。
+0

第二種方法被認爲是有效的,它基本上意味着如果用@Secured註釋裝飾一個類,每個reqauest都必須針對db進行驗證。 – naoru 2013-05-15 13:32:32

+0

基本上,是的。但它也允許跟蹤未登錄的用戶。如果客人可以做的事情,如果客人註冊永久保存的東西很有用。 – nkr 2013-05-15 16:53:45

4
  • 我建議有一個模塊,這將產生會話ID爲您服務。 在這個模塊中,你可以有一些像createSessionId()之類的方法。 生成會話Id的邏輯你保留在這個方法中。

  • 我會創建會話ID作爲(userId + providerId(Facebook /谷歌 - 在OAuth/UsernamePassword /任何提供者的情況下)+當前時間戳+ UUID)的組合和創建此會話ID後,我會加密它用一些算法。這會給我的會話ID

  • 與優勢,這將是:

    • 雖然產生的會話ID都需要時間,沒有身體會使它的意義。
    • 另一個好處是,您可以隨時在createSessionId()方法中創建會話ID,您可以更改的加密邏輯/策略

  • 在Playframework會議的另一個問題是沒有會話沒有終止:
    • 爲了解決這個問題,只要在,我們可以存儲時間戳會議,即只是在cookie的用戶登錄(通過加密可能?)
    • 現在在會話的每個請求檢查時間戳。如果時間戳大於30分鐘,則使會話無效。如果時間戳超過30分鐘,更新時間戳在會話當前時間戳不大於