在 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 的存活時間。

詳閱官網說明: http://php.net/manual/en/session.configuration.php

常用函數

函數名稱 說明
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() 檢查目前是否已經有此變數。

詳閱官網說明: http://php.net/manual/en/ref.session.php

運作原理

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 機制了,經過一系列測試確實能達成原本的需求。