跳轉到內容

PHP 程式設計/會話

來自華夏公益教科書,開放的書籍,為開放的世界

會話 允許 PHP 指令碼在 Web 伺服器上儲存資料,這些資料可以在以後使用,即使是在對不同 PHP 頁面進行請求之間也是如此。每個會話都有一個不同的識別符號,它以 Cookie 或 $_GET 變數的形式傳送到客戶端的瀏覽器。會話在使用者關閉瀏覽器時結束,或者在 Web 伺服器刪除會話資訊時結束,或者在程式設計師顯式地銷燬會話時結束。在 PHP 中,它通常被稱為 PHPSESSID。會話非常有用,可以保護使用者無法讀取或寫入的資料,尤其是當 PHP 開發人員不想在 Cookie 中提供資訊時,因為 Cookie 很容易被讀取。會話可以透過 $_SESSION 超級全域性變數來控制。儲存在此陣列中的資料在整個會話期間都是持久的。它是一個簡單的陣列。會話比 Cookie 更容易使用,這為 PHP 開發人員提供了很大幫助。大多數情況下,會話用於使用者登入、購物車和其他需要保持瀏覽流暢的補充。PHP 指令碼可以輕鬆地控制傳送的會話 Cookie,並控制整個會話資料。會話始終儲存在一個唯一的檔名中,要麼在臨時資料夾中,要麼在特定資料夾中,如果指令碼指示這樣做的話。

使用會話

[編輯 | 編輯原始碼]

在每個將作為當前會話一部分的 PHP 指令碼的開頭,必須有函式 session_start()。它必須在第一個輸出(echo 或其他)之前,否則會導致錯誤“Headers already sent out”。

 session_start();

此函式將執行以下操作

  1. 它將檢查 _COOKIE_GET 資料,如果給定的話
  2. 如果會話檔案不存在於 session.save_path 位置,它將
    1. 生成一個新的唯一識別符號,並且
    2. 根據該識別符號建立一個新檔案,並且
    3. 向客戶端瀏覽器傳送一個 Cookie
  3. 如果存在,PHP 指令碼將嘗試將檔案的資料儲存到 _SESSION 變數中,以便進一步使用

現在,您可以透過兩種不同的方式簡單地設定變數,預設方法

 $_SESSION['example'] = "Test";

或者過時的方法

 $example="Test";
 session_register($example);

以上兩個語句都將註冊會話變數 $_SESSION['example'] 為“Test”。過時的方法不應該使用,它只列出來是因為您仍然可以在由不知道新方法的程式設計師編寫的指令碼中看到它。預設方法是首選。

會話配置選項

[編輯 | 編輯原始碼]

PHP 會話很容易控制,並且可以透過一些小因素使其更安全或更不安全。以下是一些可以使用 php_ini() 函式輕鬆更改的執行時選項

名稱 預設值 可更改
session.save_path "/tmp" PHP_INI_ALL
session.name "PHPSESSID" PHP_INI_ALL
session.save_handler "files" PHP_INI_ALL
session.auto_start "0" PHP_INI_ALL
session.gc_probability "1" PHP_INI_ALL
session.gc_divisor "100" PHP_INI_ALL
session.gc_maxlifetime "1440" PHP_INI_ALL
session.serialize_handler "php" PHP_INI_ALL
session.cookie_lifetime "0" PHP_INI_ALL
session.cookie_path "/" PHP_INI_ALL
session.cookie_domain "" PHP_INI_ALL
session.cookie_secure "" PHP_INI_ALL
session.use_cookies "1" PHP_INI_ALL
session.use_only_cookies "0" PHP_INI_ALL
session.referer_check "" PHP_INI_ALL
session.entropy_file "" PHP_INI_ALL
session.entropy_length "0" PHP_INI_ALL
session.cache_limiter "nocache" PHP_INI_ALL
session.cache_expire "180" PHP_INI_ALL
session.use_trans_sid "0" PHP_INI_SYSTEM/PHP_INI_PERDIR
session.bug_compat_42 "1" PHP_INI_ALL
session.bug_compat_warn "1" PHP_INI_ALL
session.hash_function "0" PHP_INI_ALL
session.hash_bits_per_character "4" PHP_INI_ALL
url_rewriter.tags "a=href,area=href,frame=src,input=src,form=fakeentry" PHP_INI_ALL

一個簡單的例子是這段程式碼

 //Setting The Session Saving path to "sessions", '''must be protected from reading'''
 session_save_path("sessions"); // This function is an alternative to ini_set("session.save_path","sessions");
 //Session Cookie's Lifetime ( not effective, but use! )
 ini_set("session.cookie_lifetime",time()+60*60*24*500);
 //Change the Session Name from PHPSESSID to SessionID
 session_name("SessionID");
 //Start The session
 session_start();
 //Set a session cookie ( Required for some browsers, as settings that had been done before are not very effective 
 setcookie(session_name(), session_id(), time()+3600*24*365, "/");

這個例子只是將 Cookie 設定為下一年。

結束會話

[編輯 | 編輯原始碼]

當用戶點選“登出”或“退出”時,通常需要銷燬所有登入資料,這樣就沒有人可以再訪問它了。會話檔案將被刪除,同時也會透過以下方式取消設定 Cookie

 session_destroy();

使用其他型別會話資料

[編輯 | 編輯原始碼]

簡單資料,如整數、字串和陣列,可以輕鬆地儲存在 $_SESSION 超級全域性陣列中,並且可以在頁面之間傳遞。但是,當嘗試透過賦值儲存物件的 state 時,會出現問題。可以透過使用 serialize() 函式將物件的 state 儲存在會話中。serialize() 將把物件的 data 寫入一個數組中,然後可以儲存在 $_SESSION 超級全域性變數中。unserialize() 可用於在嘗試在屬於當前會話的頁面中訪問物件之前恢復物件的 state。如果要在會話期間跨多個頁面訪問使用物件,則必須在呼叫 unserialize() 之前定義物件定義。在序列化和反序列化物件時,可能會出現其他問題。

避免會話固定

[編輯 | 編輯原始碼]

會話固定描述了一種攻擊向量,其中惡意第三方設定(即固定)使用者的會話識別符號(SID),從而能夠訪問該使用者的會話。在會話的基本實現中,如上所述,這是一個非常真實的漏洞,每個使用會話進行任何敏感操作的 PHP 程式都應該採取措施來解決它。以下按適用範圍的廣度排序,是防止會話固定的措施

  1. 不要使用 GET 或 POST 變數來儲存會話 ID(在大多數 PHP 配置中,使用 Cookie 來儲存 SID,因此程式設計師不需要執行任何操作來實現這一點);
  2. 在每個使用者請求上重新生成 SID(在會話的開頭使用 session_regenerate_id());
  3. 使用會話超時:對於每個使用者請求,儲存當前時間戳,並在下一次請求時檢查是否已超過超時時間間隔;
  4. 提供登出功能;
  5. 檢查每個請求的“瀏覽器指紋”。這是一個儲存在 $_SESSION 變數中的雜湊,包含使用者代理頭、客戶端 IP 地址、鹽值和/或其他資訊的組合。有關此詳細資訊的更多討論,請參見下文;一些人認為它不過是一種“透過模糊來實現安全”。[待辦事項]
  6. 檢查來源:這並不適用於所有系統,但如果您知道您網站的使用者必須來自某個已知域,您可以丟棄與來自其他地方的使用者關聯的會話。它依賴於使用者代理提供來源頭,這一點不應該假設。
此示例程式碼解決了上述所有問題,除了來源檢查。
$timeout = 3 * 60; // 3 minutes
$fingerprint = md5('SECRET-SALT'.$_SERVER['HTTP_USER_AGENT']);
session_start();
if ( (isset($_SESSION['last_active']) && (time() > ($_SESSION['last_active']+$timeout)))
     || (isset($_SESSION['fingerprint']) && $_SESSION['fingerprint']!=$fingerprint)
     || isset($_GET['logout']) ) {
    do_logout();
}
session_regenerate_id(); 
$_SESSION['last_active'] = time();
$_SESSION['fingerprint'] = $fingerprint;
do_logout() 函式銷燬會話資料並取消設定會話 Cookie。


華夏公益教科書