2024 年日本 PHP 研討會

工作階段管理基本概念

工作階段安全性

工作階段模組無法保證儲存在工作階段中的資訊只會被建立該工作階段的使用者檢視。根據與其關聯的值,需要額外的措施來保護工作階段的機密性。

評估在 Session 中傳輸資料的重要性,並可能需要部署進一步的保護措施;這通常會付出一些代價,例如降低使用者便利性。舉例來說,為了保護使用者免受簡單的社交工程攻擊,需要啟用 session.use_only_cookies。在這種情況下,用戶端必須無條件啟用 Cookie,否則 Session 將無法運作。

有幾種方法會將現有的 Session ID 洩漏給第三方,例如 JavaScript 注入、URL 中的 Session ID、封包嗅探、對裝置的實體存取等等。洩漏的 Session ID 可讓第三方存取與特定 ID 相關聯的所有資源。首先,攜帶 Session ID 的 URL。如果存在指向外部網站或資源的連結,包含 Session ID 的 URL 可能會儲存在外部網站的參考來源記錄中。其次,更積極的攻擊者可能會監聽網路流量。如果未加密,Session ID 將以純文字形式在網路上傳輸。解決方案是在伺服器上實作 SSL/TLS,並強制使用者使用。為了提高安全性,應使用 HSTS。

注意即使是 HTTPS 也無法隨時保護機密資料。例如,CRIME 和 Beast 漏洞可能讓攻擊者讀取資料。此外,請注意,許多網路為了審計目的而使用 HTTPS 中間人代理伺服器。攻擊者也可能設置這樣的代理伺服器。

非適應性 Session 管理

PHP 的 Session 管理器目前預設為適應性。適應性 Session 管理器帶有額外的風險。

當啟用 session.use_strict_mode 且 Session 儲存處理程式支援它時,未初始化的 Session ID 將被拒絕,並建立新的 Session ID。這可以防止攻擊者強制使用者使用已知的 Session ID 的攻擊。攻擊者可能會貼上包含 Session ID 的連結或傳送電子郵件。例如,如果啟用了 session.use_trans_sidhttp://example.com/page.php?PHPSESSID=123456789,受害者將使用攻擊者提供的 Session ID 啟動 Session。session.use_strict_mode 可以降低此風險。

警告

使用者自訂的儲存處理程式也可以透過實作 Session ID 驗證來支援嚴格 Session 模式。所有使用者自訂的儲存處理程式都應實作 Session ID 驗證。

Session ID Cookie 可以設定網域、路徑、httponly、secure 以及從 PHP 7.3 開始的 SameSite 屬性。瀏覽器定義了優先順序。利用優先順序,攻擊者可以設定可永久使用的 Session ID。使用 session.use_only_cookies 無法解決此問題。session.use_strict_mode 可以降低此風險。啟用 session.use_strict_mode 後,未初始化的 Session ID 將被拒絕。

注意事項即使 session.use_strict_mode 降低了適應性工作階段管理的風險,攻擊者仍可能強制用戶使用由攻擊者創建的已初始化工作階段 ID,例如透過 JavaScript 注入。本手冊的建議可以減輕這種攻擊的風險。 開發者應遵循本手冊的建議,啟用 session.use_strict_mode,使用基於時間戳記的工作階段管理,並使用建議的程序搭配 session_regenerate_id() 重新產生工作階段 ID。如果開發者遵循以上所有步驟,攻擊者產生的工作階段 ID 最終將會被刪除。 當存取到過期的工作階段時,開發者應儲存使用者所有有效的工作階段資料,因為這些資訊與後續的調查有關。應強制使用者登出所有工作階段,即要求他們重新驗證。這可以防止攻擊者濫用竊取的工作階段。

警告

存取過期的工作階段並不一定代表遭受攻擊。網路不穩定和/或立即刪除有效的工作階段都可能導致合法使用者使用過期的工作階段。

自 PHP 7.1.0 起,新增了 session_create_id() 函式。透過使用使用者 ID 作為工作階段 ID 的前綴,此函式可以有效地存取使用者所有有效的工作階段。在此設定下,啟用 session.use_strict_mode 至關重要。否則,惡意使用者可以為其他使用者設定惡意工作階段 ID。

注意事項PHP 7.1.0 之前的使用者*應該*使用CSPRNG,例如 /dev/urandomrandom_bytes() 以及雜湊函式來產生新的工作階段 ID。session_create_id() 具有碰撞偵測功能,並根據工作階段的 INI 設定產生工作階段 ID。建議使用 session_create_id()

工作階段 ID 重新產生

session.use_strict_mode 是一個很好的緩解措施,但還不夠。開發者同樣必須使用 session_regenerate_id() 來確保工作階段安全性。

重新產生工作階段 ID 可以降低工作階段 ID 被盜用的風險,因此必須定期呼叫 session_regenerate_id()。例如,對於安全性敏感的內容,每 15 分鐘重新產生一次工作階段 ID。即使工作階段 ID 被盜用,合法使用者和攻擊者的工作階段都會過期。換句話說,使用者或攻擊者的存取將會產生過期工作階段存取錯誤。

當使用者權限提升時,例如驗證後,*必須*重新產生工作階段 ID。session_regenerate_id() 必須在將驗證資訊設定到 $_SESSION 之前呼叫。(session_regenerate_id() 會自動儲存目前的工作階段資料,以便將時間戳記等資訊儲存到目前的工作階段。)確保只有新的工作階段包含已驗證的旗標。

開發者*不得*依賴 session.gc_maxlifetime 的工作階段 ID 過期機制。攻擊者可能會定期存取受害者的工作階段 ID,以防止其過期並持續利用它,包括已驗證的工作階段。

相反地,開發者必須實作基於時間戳記的工作階段資料管理。

警告

雖然工作階段管理器可以透明地管理時間戳記,但此功能尚未實作。舊的工作階段資料必須保留到垃圾回收 (GC) 執行。同時,開發者必須確保過時的工作階段資料會被移除。然而,開發者不得立即移除有效的工作階段資料。例如,針對有效的工作階段,`session_regenerate_id(true);` 和 session_destroy() 絕對不能一起呼叫。這聽起來可能相互矛盾,但這是一項強制性要求。

session_regenerate_id() 預設*不會*刪除過時的工作階段。過時的已驗證工作階段可能仍存在可供使用。開發者必須防止過時的工作階段被任何人使用。他們必須使用時間戳記自行禁止存取過時的工作階段資料。

警告

突然移除有效的工作階段會產生不良副作用。當有多個並行連線到網路應用程式和/或網路不穩定時,工作階段可能會消失。

若突然移除有效的工作階段,則潛在的惡意存取將無法偵測。

開發者不應立即刪除過時的工作階段,而必須在 $_SESSION 中設定一個短期到期時間(時間戳記),並自行禁止存取該工作階段資料。

開發者不得在呼叫 session_regenerate_id() 後立即禁止存取舊的工作階段資料。必須在稍後階段禁止存取。例如,對於穩定的網路(如有線網路)幾秒鐘後,對於不穩定的網路(如行動電話或 Wi-Fi)幾分鐘後。

如果使用者存取過時的工作階段(已過期的工作階段),則應拒絕其存取。同時建議移除使用者所有工作階段的驗證狀態,因為這很可能代表著攻擊行為。

正確使用 session.use_only_cookiessession_regenerate_id() 可能會導致攻擊者設定無法刪除的 Cookie,造成個人層面的阻斷服務 (DoS) 攻擊。在這種情況下,開發者可以建議使用者移除 Cookie,並告知他們可能受到安全問題的影響。攻擊者可能透過存在漏洞的網路應用程式、已暴露/惡意的瀏覽器外掛程式、遭到物理入侵的裝置等方式設定惡意 Cookie。

警告

不要誤解阻斷服務 (DoS) 的風險。對於一般工作階段 ID 安全性而言,session.use_strict_mode=On 是強制性的!建議所有網站都啟用 session.use_strict_mode

只有在帳戶受到攻擊時才會發生阻斷服務 (DoS)。應用程式中的 JavaScript 注入漏洞是最常見的原因。

工作階段資料刪除

過時的工作階段資料必須無法存取且必須被刪除。目前的工作階段模組在這方面處理得不夠完善。

過時的工作階段資料應盡快移除。然而,有效的工作階段不得立即移除。為了滿足這些需求,開發者必須自行實作基於時間戳記的工作階段資料管理。

在 $_SESSION 中設定和管理到期時間戳記。禁止存取過時的工作階段資料。當偵測到存取過時的工作階段資料時,建議移除使用者所有工作階段的驗證狀態,並強制他們重新驗證。存取過時的工作階段資料可能代表著攻擊行為。為了達成此目的,開發者必須追蹤每個使用者的所有有效工作階段。

注意 不穩定的網路和/或同時存取網站也可能導致存取到過期的工作階段。例如,伺服器嘗試透過 Cookie 設定新的工作階段 ID,但由於連線中斷,Set-Cookie 封包可能沒有到達用戶端。一個連線可能會透過 session_regenerate_id() 發出新的工作階段 ID,但另一個同時發生的連線可能尚未收到新的工作階段 ID。因此,開發人員必須在後續階段禁止存取過期的工作階段。也就是說,基於時間戳記的工作階段管理是必要的。

總之,工作階段資料不能使用 session_regenerate_id()session_destroy() 銷毀,而必須使用時間戳記來控制對工作階段資料的存取。讓 session_gc() 從工作階段資料儲存區中移除過期的資料。

工作階段與鎖定

預設情況下,工作階段資料會被鎖定以避免競爭條件。鎖定是必要的,以保持工作階段資料在不同請求之間的一致性。

然而,攻擊者可能會濫用工作階段鎖定來執行 DoS 攻擊。為了降低工作階段鎖定導致 DoS 攻擊的風險,請盡量減少鎖定。當工作階段資料不需要更新時,請使用唯讀工作階段。在 session_start() 中使用 'read_and_close' 選項。session_start(['read_and_close'=>1]); 更新 $_SESSION 後,請盡快使用 session_commit() 關閉工作階段。

目前的工作階段模組在工作階段閒置時*不會*偵測 $_SESSION 的任何修改。開發人員有責任在工作階段閒置時不要修改 $_SESSION。

活動工作階段

開發人員應追蹤每個使用者的所有活動工作階段。並通知他們活動工作階段的數量、來自哪個 IP(和地區)、活動時間長度等。PHP 並不會追蹤這些資訊。開發人員應該自行處理。

有各種方法可以實現這一點。一種可能的實現方法是設定一個資料庫來追蹤所需的資料並儲存任何相關資訊。由於工作階段資料會被 GC 回收,開發人員必須處理被 GC 回收的資料,以維護活動工作階段資料庫的一致性。

最簡單的實現方法之一是「使用者 ID 作為前綴的工作階段 ID」,並將所需的資訊儲存在 $_SESSION 中。許多資料庫在選擇字串前綴方面都具有良好的效能。開發人員*可以*使用 session_regenerate_id()session_create_id() 來實現這一點。

警告

切勿使用機密資料作為前綴。如果使用者 ID 是機密的,請考慮使用 hash_hmac()

警告

啟用 session.use_strict_mode 對於此設定是必要的。請確保已啟用它。否則,活動工作階段資料庫可能會遭到入侵。

基於時間戳記的工作階段管理對於偵測對過期工作階段的存取是必要的。當偵測到對過期工作階段的存取時,應從使用者的所有活動工作階段中移除驗證旗標。這可以防止攻擊者繼續利用被盜的工作階段。

工作階段與自動登入

開發人員不得使用長效工作階段 ID 進行自動登入,因為這會增加工作階段被盜的風險。自動登入功能應由開發人員自行實作。

使用安全的單次雜湊金鑰作為自動登入金鑰,並使用 setcookie() 函式設定。請使用比 SHA-2 更強的雜湊演算法,例如 SHA-256 或更高版本,搭配 random_bytes()/dev/urandom 產生的隨機資料。

如果使用者未經驗證,請檢查單次自動登入金鑰是否有效。如果有效,則驗證使用者並設定新的安全單次雜湊金鑰。自動登入金鑰只能使用一次,也就是說,永遠不要重複使用自動登入金鑰,務必產生新的金鑰。

自動登入金鑰是一個長期有效的驗證金鑰,應盡可能受到保護。使用 path/httponly/secure/SameSite 等 Cookie 屬性來保護它。也就是說,除非必要,否則永遠不要傳輸自動登入金鑰。

開發者必須實作停用自動登入和移除不需要的自動登入金鑰 Cookie 的功能。

CSRF (跨站請求偽造) 攻擊

Session 和驗證機制無法防禦 CSRF 攻擊。開發者必須自行實作 CSRF 防護措施。

output_add_rewrite_var() 函式可用於 CSRF 防護。詳情請參閱使用手冊。

注意:7.2.0 之前的 PHP 版本使用與 trans sid 相同的輸出緩衝區和 INI 設定。因此,不建議在 7.2.0 之前的 PHP 版本中使用 output_add-rewrite-var() 函式。

大多數 Web 應用程式框架都支援 CSRF 防護。詳情請參閱 Web 應用程式框架的使用手冊。

從 PHP 7.3 開始,可以設定 Session Cookie 的 SameSite 屬性。這是一項額外的措施,可以降低 CSRF 漏洞的風險。

新增註記

使用者提供的註記

此頁面沒有使用者提供的註記。
To Top