![PHP Session 運作原理 - 封面圖](https://blog.tarswork.com/upload/filemanager/post/2021/3_thumbnail.webp)
PHP Session 運作原理
網頁開發在 PHP 程式語言中,Session 與 Cookie 的使用方式相當類似,兩者都是用來儲存使用者的一些資訊。一般認為 Session 儲存於伺服器上,應該比較安全,但如果把所有資訊都存放在 Session 中,當用戶數量增加時,勢必會造成伺服器資源大量消耗。因此,在實務上,常會將敏感資訊使用 Session 儲存,而不太重要的資訊或控制網站狀態的參數則會放在 Cookie 中儲存。所有網路上流通的資訊都不可能是絕對安全的,但也千萬不要因為安全性考量而將所有的資訊都使用 Session 來儲存,應該考量實際的系統功能,在 Session 與 Cookie 當中取得平衡才是正確的作法。當然伺服器必要的安全設定及防火牆也都要設定好,才能確保伺服器的安全性。
Session 與 Cookie 使用上是相當類似的,大致上,我們可以這樣區別:
- Session
- 儲存於伺服器、使用伺服器資源、不適合長久儲存。可儲存的容量較大,視伺服器設定而定。
- Cookie
- 儲存於用戶端、使用用戶端資源、可長久儲存。可儲存的容量較小,且需擔心用戶禁用 Cookie。
PHP設定檔
設定名稱 | 說明 | |
---|---|---|
1 | session.save_handler | 定義存儲 Session 的處理器名稱。 |
2 | session.save_path | 定義存儲 Session 的位置。 |
3 | session.name | 定義 Session 儲存在 Cookie 的變數名稱。 |
4 | session.auto_start | 自動啟動 Session。 |
5 | session.gc_probability | 與 gc_divisor 使用,用來設定垃圾回收的機率。 |
6 | session.gc_divisor | 與 gc_probability 使用,用來設定垃圾回收的機率。 |
7 | session.gc_maxlifetime | 設定 Session 逾期時間。 |
8 | session.cookie_lifetime | 設定 SessionID 儲存在 Cookie 的存活時間。 |
常用函數
函數名稱 | 說明 | |
---|---|---|
1 | session_start() | 啟動一個新的或正在使用中的 Session。 |
2 | session_destroy() | 清除全部 Session,包含儲存在 Server 的 Session 檔案也會被刪除。 |
3 | session_name() | 取得目前或更新 Session 變數名稱。 |
4 | session_id() | 取得目前或更新 SessionID。 |
5 | session_save_path() | 取得目前使用中的 Session 路徑。 |
6 | session_is_registered() | 檢查目前是否已經有此變數。 |
運作原理
Session 啟動時會在 Server 端產生一個格式為 sess_[SessionID] 的檔案存放在 php.ini 設定好的路徑,同時將 SessionID 透過 Cookie 儲存在 Client 端,之後 Client 端每次發送 Request 時,都會將 SessionID 傳回 Server,這樣 Server 端就可以根據 SessionID 來識別是哪一個用戶的儲存資訊。當有很多使用者在存取網站的時候,每個使用者都會被分配到一個唯一的 SessionID,這樣可以確保每個使用者的資料都是獨立的不會互相干擾。所以可想而知 SessionID 在 Server 端的 Session 還沒有清除的狀況下就被駭客擷取的話,這樣駭客是有辦法利用這個 SessionID 偽裝成該使用者而竊取走 Session 中的重要資料。
當使用者禁用 Cookie
Session 的運作預設是需要搭配 Cookie 來使用的,當 Client 端禁用 Cookie 的時候,將使得 SessionID 無法存儲於 Client 端,這時候可以將 php.ini 的 session.use_trans_sid 設定為 1 (表示啟用),這樣 SessionID 就可以透過網址的方式傳送回 Server 而無須透過 Cookie。
Session 失效的機制
PHP Session 失效的機制很特別,讓我們先了解一下 php.ini 中的幾項與 Session 有關的設定值。
session.cookie_lifetime = 0 // 0 表示 Cookie 存活至關閉瀏覽器
session.gc_probability = 1
session.gc_divisor = 100
session.gc_maxlifetime = 1440 // 預設 Session 逾期時間為 24 分鐘
經測試 cookie_lifetime 是指 Session 在 Client 端的存活時間,假如這個值設定為 60 秒的話,代表你不管怎麼使用網站,Session 一定會在 60 秒後失效。假如 cookie_lifetime 設定為 0、gc_maxlifetime 設定為 1440 秒,代表 Client 端在沒有操作網站的狀況下超過 1440 秒,Session 即有機會失效。這邊會說有機會失效是因為 PHP 的垃圾回收機制是根據 gc_probability 與 gc_divisor 來設定的,假如 gc_probability = 1、gc_divisor = 100,代表 Client 端送出 Request 有 1% 的機率使逾期的 Session 失效。
Session 操作
session_start(); // 啟動 Session
$_SESSION["A"] = 10; // 將資料存放在 SESSION
echo $_SESSION["A"]; // 取得 SESSION 值
unset($_SESSION["A"]); // 刪除單一 SESSION 變數
session_destroy(); // 刪除所有 Session 變數及檔案
session_start() 啟動前不能有任何輸出,不然會收到 Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent 的警告訊息。另外盡量使用 unset() 來刪除單一 Session 變數,如果要刪除該使用者的所有 Session 請使用 session_destroy(),這個方法會將 Session 清除也會把存在 Server 的檔案也一併刪除了。大家可以將上面程式執行看看,看有沒有 session_destroy() 檔案是否有存在。
Session 預設儲存路徑
作業系統 | 路徑 |
---|---|
Windows | C:\Windows\Temp\ |
Linux | /var/lib/php/session/ |
實際案例分享
之前為了這個機制花了很多時間研究,當初的需求是要讓使用者登入後可容許逾時15分鐘,但是逾時15後就一定得登出。原本想說將設定修改如下:
session.cookie_lifetime = 86400
session.gc_probability = 100
session.gc_divisor = 100
session.gc_maxlifetime = 900
此方法確實可以達成我們要的效果,可是後來再做壓力測試時,非常耗效能,可想而知每次 Request 必定刪除失效的 Session,所以效能一定消耗很大,後來參考了 php.ini 上面的說明用了另一種方式實作,設定檔如下:
session.cookie_lifetime = 86400
session.gc_probability = 1
session.gc_divisor = 100
session.gc_maxlifetime = 900
接下來利用系統排程來刪除 Session,編輯 /etc/crontab,新增以下排程:
0 * * * * root find /var/lib/php/session/ -cmin +15 -type f | xargs --no-run-if-empty rm
15 * * * * root find /var/lib/php/session/ -cmin +15 -type f | xargs --no-run-if-empty rm
30 * * * * root find /var/lib/php/session/ -cmin +15 -type f | xargs --no-run-if-empty rm
45 * * * * root find /var/lib/php/session/ -cmin +15 -type f | xargs --no-run-if-empty rm
此種方式是讓系統去刪除 Session,而不透過 PHP GC 機制了,經過一系列測試確實能達成原本的需求。
0 則留言