如何管理Session(防止惡意共享賬號)——理論篇


目錄

  • 知識要求
  • 背景
  • 技術原理
  • 如何管理Session
  • remember me的問題
  • TODO
  • 附錄

知識要求

  • 有一定的WEB后端開發基礎,熟悉Session的用法,以及與RedisDatabase的配合
  • 本文的原理討論基於PHPLaravel,盡管原理是通用的,但是讀者具備相關知識理解會更輕松

背景

公司在業務層面上,通常會期望自己運營的系統,一個注冊帳號只能由本人使用或者少數幾個人共用。實際情況卻是:有些用戶會將注冊帳號的用戶名和密碼共享給成百上千個用戶;也有的用戶不直接提供用戶名和密碼,而是提供帶有認證信息的cookie給其他用戶,同樣達到共享賬號的目的。不論哪種形式,都造成了公司業務的損失。因此系統應該具備查看在線用戶的功能,並可對在線用戶實時管理,防止注冊帳號被許多人共享。

技術原理

在技術層面上,在線用戶都是用Session表示。

用戶通過用戶名和密碼正常登陸時,就會產生新的Session,退出則對應Session被清除。當多個用戶使用同一賬號登陸時,就會產生多個Session,防止共享賬號數量太多就是要限制同一賬號的Session數量。但是,這遠遠不夠。

我們都知道,Session的原理是通過將session id寫入cookie,下次瀏覽器訪問時會把cookie帶上來,識別其中的session id實現的(laravel存儲session_idcookie名稱為laravel_session)。如果將該session id傳遞給其他用戶,其他用戶再將session id寫入cookie,就可以達到共享賬號,同時Session仍然是同一個的目的(見下圖流程)。這種情況使得在線用戶管理變得棘手,好在它們的IP並不相同,所以還是有辦法處理。

通過cookie共享Session原理

上面兩種形式是共享賬號的兩種主要形式,於是我們的問題就變成:

  1. 如何管理一個賬號對應多個Session的問題
  2. 如何管理一個Session多個IP的問題

下面先對如何管理Session做綜述,再對兩種情況分開說明。

如何管理Session

管理Session的前提是

  • 系統能夠獲取到所有的Session
  • 獲取Session的所有IP信息。

一個高性能的系統,Session保存在緩存系統,比如Redis中。通過統一約定以session.開頭的鍵為Session,就可以獲取到所有Session,但這實際上是個很差的方法。Redis的設計並不是為了實現這樣的目的,所以它的鍵值匹配要么效率極低,要么不能保證返回所有結果,同時它的擴展性非常地差,比如只讀取某個用戶的所有Session,需要對鍵的命名再做進一步約束。

於是我們換了另外一種方案,Session仍然保存在緩存系統中,同時異步保存在數據庫中,注意必須是異步,否則會影響系統的運行。具體原理是,在Session的寫入、銷毀、回收這幾個階段發出event,將session連同HTTP請求放到隊列中(隊列是上下文無關的,獲取不到任何HTTP請求的信息,需要從事件中讀取),然后隊列取出這些事件,寫入到數據庫。這樣就能做到不影響性能,又可獲取到所有的Session信息,並做靈活地管理。

Session默認沒有攜帶IP信息,因此在每次Session寫入時,需要再做一層加工,將IP寫入Session,並且不能只保存一個IP,需要保存多個,以便后續問題的處理。

1.如何管理一個賬號對應多個Session的問題

既然數據庫中已經保存了所有Session,在有新的Session產生時,檢查是否超出指定數量。當超出時,自動刪除最早的Session即可。

如何手動測試

設置好要限制的數量,假設為2。安裝SessionBoxChrome插件),創建3個窗口以相同用戶登陸,將發現最早登陸的窗口刷新后處於未登陸狀態。

合理的Session數量

一個用戶可能從多個設備登陸,比如PC、手機、平板,所以Session數量至少在3個以上。用戶也可能在PC上開N個不同瀏覽器,導致同一個設備有多個Session,應該優化此種情況,判定為同一個設備。具體看《TODO》這一節說明。

2.如何管理一個Session多個IP的問題

所有的用戶共享同一個Session,也就沒辦法精確控制要保留的數量了。要么刪除Session,所有用戶重新登陸;要么重新生成session id,只保留一個用戶,其他所有用戶需要重新登陸。目前采用后者,因為前者有一個風險,同一個用戶可能從多個地方登陸產生了大量的IP,結果就踢出去了,用戶體驗不好。而如果是后者,如果一個用戶,只是自己使用,那么不論他的ip數量是否突破限制,重新生成session id仍然是他的,所以不會受影響。

它的原理是用戶發起請求,發現IP過多,就重新生成session id,這個新的session id會寫到該用戶的cookie中,而其他用戶由於沒有這個新的session id,所以需要重新登陸。

從這個流程中也可看出,這個處理過程必須是在用戶發起請求時處理。需要注意的是,這個過程會需要考慮並發,即便是單個用戶訪問。假設一個用戶訪問頁面,該頁面同時發起4個請求。服務端同時處理這4個請求,都發現sessionip過多,於是刪除舊session重新生成,造成一個問題:4個請求刪除同一個舊session,然后生成了4個不同的session。為防止這種情況,使用了Redis對該session id加鎖,並設置30秒自動過期,只有第一個獲取鎖的人執行重新生成,其他沒獲得鎖的請求不處理。然后鎖不用釋放,自然過期即可。

正常而言,session已經被重新生成了,舊session id是走不到加鎖session id這一步的。如果有,那一定是在重新生成之前就進來的請求,而這些請求本來就應該被忽略。反之,如果刪除鎖,這些請求將再次加鎖並重新生成session,仍然會造成剛才說的問題,因此直接讓鎖自動過期即可。

如何手動測試

假設IP數量限制為1個,打開A``B兩台電腦,在A電腦上先登陸,打開Chrome開發者工具,復制laraval_session的值;然后傳到B電腦,打開Chrome開發者工具,設置laravel_session的值,然后刷新下將發現變為登陸狀態。再刷新下A電腦,將發現處於未登陸狀態。

合理的ip限制數量

這個值則很主觀,同時多個用戶共享一個Session的問題實際是可避免的,具體參考《Remember Me的問題》的說明。因此不建議設置得太小,建議在5以上。

remember me的問題

如果一個賬號在線的存活期只有幾個小時,那么上面說的問題影響范圍都有限。為提高用戶體驗,用戶一次登陸后可存活好幾天甚至永久存活。提高存活時間有兩種方法:

  1. 修改Session的過期時間
  2. 使用Remember Me的機制

上面管理Session的方案,在第1種方法下能順利工作,但是在第2種方法下則沒法工作。所以使用了Remember Me的方案,在管理機制上需要重新設計。

要理解這個問題所在,我們需要理解Remember Me的機制:用戶登陸后,如果設置了Remember Me,服務器會生成remember me token,保存在數據庫中,並將該token寫入到cookie中。用戶Session過期后,再次訪問瀏覽器,服務端發現cookie中的remember me信息與數據庫中的一致,就重新生成新的Session(見流程圖)。

通過Remember永久登陸原理

在了解上述機制后,即可發現,當remember me的用戶超過session限制數量后,最早的session被刪除,但由於該用戶有remember me,所以會重新生成session自動恢復,也就說,刪除sessionremember me用戶無效,會立刻重新生成。所以上述的session管理方案不應該開啟remember me,否則是有問題的。

那為什么不直接Remember Me的方案呢?主要原因在於它的設計比較復雜,最終我們會切換成Remember Me的機制,將會另開一篇專門討論。

TODO

  • [ ] 多個Session可能是同一個設備發出的,因此應該結合IP判斷是否是同一個設備,或者結合客戶端發出的唯一標識(唯一標識需要是PC的唯一標識)。如果是同一個設備的就都放過的話,其實也有問題,SessionBox這樣的工具可以在單個瀏覽器創建多個Session,如果這個過程腳本化,服務器會產生大量垃圾Session,所以也應該限制數量。
  • [ ] 改為Remember me方案,將問題簡化為只考慮一個帳戶多個Session的問題。
  • [ ] 客戶端發送唯一標識的方式是否具備可行性?

附錄

知識點

Q:Session與登陸用戶的關系?

A:嚴格來說,只要用戶打開瀏覽器訪問網站,就會產生Session標識一個會話,跟是否登陸無關。但是一般情況下,我們只關心登陸用戶的Session,因此這里討論上不做區分,只要產生Session就認為有登陸用戶。

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM