會話固定
在電腦網路安全中,會話固定(Session Fixation)攻擊試圖利用系統的漏洞,讓攻擊者得以固定(找到或設定)另一位使用者的會話識別碼。大多數的會話固定攻擊是基於網站的,且多數仰賴從 URL(查詢字串)或 POST 資料中接受會話識別碼。
攻擊情境
Alice 在銀行 http://unsafe.example.com/ 有一個帳戶。
Mallory 企圖竊取 Alice 在銀行的錢。
Alice 對 Mallory 有相當程度的信任,會點擊 Mallory 傳給她的連結。
簡單的攻擊情境
直接的情境:
- Mallory 發現
http://unsafe.example.com/接受任何會話識別碼、接受來自查詢字串的會話識別碼,並且沒有安全驗證。因此http://unsafe.example.com/是不安全的。 - Mallory 寄了一封電子郵件給 Alice:「嘿,快看,我們銀行有個很酷的新帳戶總覽功能,
http://unsafe.example.com/?SID=I_WILL_KNOW_THE_SID」。Mallory 企圖將 SID 固定為I_WILL_KNOW_THE_SID。 - Alice 感興趣並造訪了
http://unsafe.example.com/?SID=I_WILL_KNOW_THE_SID。正常的登入畫面出現,Alice 登入了。 - Mallory 造訪
http://unsafe.example.com/?SID=I_WILL_KNOW_THE_SID,現在他可以無限制地存取 Alice 的帳戶。
使用伺服器產生的 SID 進行攻擊
一個常見的誤解是,如果伺服器只接受由伺服器產生的會話識別碼,就能免於固定攻擊。這是錯誤的。
情境:
- Mallory 造訪
http://vulnerable.example.com/並檢查回傳的 SID。例如,伺服器可能回應:Set-Cookie: SID=0D6441FEA4496C2。 - Mallory 現在可以寄一封電子郵件給 Alice:「快看我們銀行這個很酷的新功能,
http://vulnerable.example.com/?SID=0D6441FEA4496C2。」 - Alice 使用被固定的會話識別碼
SID=0D6441FEA4496C2登入。 - Mallory 造訪
http://vulnerable.example.com/?SID=0D6441FEA4496C2,現在他可以無限制地存取 Alice 的帳戶。
使用跨子網域 cookie 的攻擊
這類攻擊與跨網站 cookie 攻擊相似,但它不依賴使用者瀏覽器的漏洞。相反地,它利用了萬用字元 cookie 可由子網域設定,且這些 cookie 可能會影響其他子網域的事實。
情境:
- 一個網站
www.example.com將子網域分發給不受信任的第三方。 - 其中一方,Mallory,現在控制著
evil.example.com,他引誘 Alice 到他的網站。 - 造訪
evil.example.com會在 Alice 的瀏覽器上設定一個網域為.example.com的會話 cookie。 - 當 Alice 造訪
www.example.com時,這個 cookie 會隨著請求一同發送,Alice 的會話將會是 Mallory 的 cookie 所指定的。 - 如果 Alice 現在登入,Mallory 就可以使用她的帳戶。
當此攻擊完成後,Mallory 就能以 Alice 的身分存取 www.example.com。
要利用會話固定攻擊,使用者登入並非必要條件,雖然這些未經驗證的攻擊不限於跨子網域 cookie 攻擊,但子網域攻擊的影響與這些未經驗證的情境相關。例如,Mallory 可能提供一個來自其惡意網站的 URL,在一個未經驗證的情境中固定一個會話,並利用這些技術來攻擊他的目標。這包括利用未經驗證的情境(例如表單或註冊)以及提供使用者一個已建立的會話以完全繞過登入的能力。
舉例來說,Mallory 可能在 www.example.com 上建立一個使用者 A1ice,並登入該使用者以獲取一個當前有效的會話識別碼。然後 Mallory 用一個來自 evil.example.com 的 URL 誘騙 Alice,該 URL 在 Alice 的瀏覽器中固定了那個會話 cookie(如上所述),並重新導向到 www.example.com 以完成某個特定交易(或者,實際上是更廣泛的用途)。Mallory 因此能夠從他最初的登入中潛伏在該會話中,在 'www.example.com' 上以 'A1ice' 的身分抓取資料和執行操作。如果 Alice 成功受騙並將她的信用卡資料儲存到該帳戶,Mallory 接著就可以用那張卡進行購物。
對策
不接受來自 GET / POST 變數的會話識別碼
不建議將會話識別碼放在 URL(查詢字串、GET 變數)或 POST 變數中,因為這會簡化此類攻擊—建立設定 GET / POST 變數的連結或表單非常容易。
- 當使用者從網址列複製並貼上「有趣的連結」到聊天室、論壇、社群等地方時,SID 會洩漏給其他人。
- SID 會儲存在許多地方(瀏覽器歷史紀錄、網頁伺服器日誌、代理伺服器日誌…)
注意:Cookie 在瀏覽器分頁和彈出視窗之間是共享的。如果您的系統需要同時存取同一個網域(www.example.com/?code=site1 和 www.example.com/?code=site2),分頁之間的 cookie 可能會互相衝突。
為了克服這個限制,可能需要在 URL 中傳送會話識別碼。如果可能,請使用 site1.example.com 或 site2.example.com,這樣 cookie 就不會有網域衝突。這可能會產生額外的 SSL 憑證費用。
這種行為可以在許多網站上觀察到,只要打開另一個分頁並嘗試並排進行搜尋。其中一個會話將會變得無法使用。
在 PHP 8.4 中,GET 和 POST 中的會話識別碼已被棄用,並將在 PHP 9.0 中移除。
最佳解決方案:身分確認
當使用者登入時,透過變更、重新產生會話 ID,可以在很大程度上避免這種攻擊。如果每個特定於使用者的請求都需要使用者通過網站驗證(「登入」),那麼攻擊者就需要知道受害者登入會話的 ID。然而,當受害者訪問帶有固定會話 ID 的連結時,他們需要登入自己的帳戶才能以自己的身分進行任何「重要」操作。此時,他們的會話 ID 將會改變,攻擊者將無法用那個匿名的會話 ID 做任何「重要」的事情。
類似的技術可以用來解決釣魚問題。如果使用者用兩個密碼保護他們的帳戶,那麼這個問題可以在很大程度上被解決。
這項技術對防禦跨站請求偽造攻擊也很有用。
解決方案:將會話識別碼儲存在 HTTP cookie 中
在大多數現代系統中,會話識別碼預設儲存在 HTTP cookie 中,只要會話系統忽略 GET/POST 值,這就具有中等程度的安全性。然而,這種解決方案容易受到跨站請求偽造的攻擊,並且不符合 REST 的無狀態要求。
解決方案:利用 SSL / TLS 會話識別碼
當啟用 HTTPS 安全性時,有些系統允許應用程式獲取 SSL / TLS 會話識別碼。使用 SSL/TLS 會話識別碼非常安全,但許多網頁開發語言並未為此提供強大的內建功能。
在每個請求上重新產生 SID
防禦會話固定的一個對策是在每個請求上產生一個新的會話識別碼(SID)。如果這樣做,即使攻擊者可能誘騙使用者接受一個已知的 SID,當攻擊者試圖重複使用該 SID 時,它也將是無效的。實現這樣的系統很簡單,如下所示:
- 從 HTTP 請求中獲取先前的會話識別碼
OLD_SID。 - 如果
OLD_SID為 null、空值,或不存在 SID=OLD_SID的會話,則建立一個新會話。 - 使用安全的亂數產生器產生新的會話識別碼
NEW_SID。 - 讓會話由 SID=
NEW_SID識別(而不再由 SID=OLD_SID識別)。 - 將新的 SID 傳送給用戶端。
範例:
如果 Mallory 成功誘騙 Alice 造訪 http://victim.example.com/?SID=I_KNOW_THE_SID,這個 HTTP 請求會被送到 victim.example.com:
<syntaxhighlight lang="http">GET /?SID=I_KNOW_THE_SID HTTP/1.1 Host: victim.example.com</syntaxhighlight>
victim.example.com 接受了 SID=I_KNOW_THE_SID,這通常是不好的。然而,victim.example.com 是安全的,因為它執行了會話重新產生。victim.example.com 得到以下回應:
<syntaxhighlight lang="http">HTTP/1.1 200 OK Set-Cookie: SID=3134998145AB331F</syntaxhighlight>
Alice 現在將使用 Mallory 不知道的 SID=3134998145AB331F,而 SID=I_KNOW_THE_SID 則是無效的。因此,Mallory 的會話固定嘗試失敗了。
不幸的是,會話重新產生並非總是可行的。當使用第三方軟體如 ActiveX 或 Java applet,以及當瀏覽器外掛程式與伺服器通訊時,已知會發生問題。第三方軟體可能導致登出,或者會話可能被分割成兩個獨立的會話。
如果會話的實作包含透過 GET 或 POST 變數傳輸 SID,那麼這也可能使大多數瀏覽器的「上一頁」按鈕無法使用,因為使用者屆時將會使用來自先前請求的一個舊的、無效的會話識別碼。
只接受伺服器產生的 SID
提高安全性的一種方法是不接受非由伺服器產生的會話識別碼。然而,如上所述,這並不能阻止所有的會話固定攻擊。
<syntaxhighlight lang="php"> if (!isset($_SESSION['SERVER_GENERATED_SID'])) {
session_destroy(); // 銷毀會話中的所有資料
session_regenerate_id(); // 產生一個新的會話識別碼 $_SESSION['SERVER_GENERATED_SID'] = true; </syntaxhighlight>
登出功能
登出功能很有用,因為它允許使用者表明一個會話不應再允許任何請求。因此,攻擊只有在會話處於活動狀態時才能生效。請注意,以下程式碼未執行跨站請求偽造檢查,這可能讓攻擊者能強迫使用者登出網站應用程式。
<syntaxhighlight lang="php"> if (logout) {
session_destroy(); // 銷毀會話中的所有資料
</syntaxhighlight>
讓舊的 SID 逾時
這種防禦方法實作簡單,並且其優點是為防止未經授權的使用者透過可能無人看管的機器存取已授權使用者的帳戶提供了一定程度的保護。
儲存一個包含該 SID 最後存取時間戳記的會話變數。當該 SID 再次被使用時,將目前的時間戳記與儲存在會話中的時間戳記進行比較。如果差異大於預先定義的數值,例如 5 分鐘,就銷毀該會話。否則,用目前的時間戳記更新該會話變數。
如果 Referrer 可疑,則銷毀會話
當造訪一個頁面時,大多數網頁瀏覽器會設定 Referrer 標頭—即包含您點擊以到達此頁面的連結的那個頁面。
當使用者登入一個不太可能從站外連結的網站(例如,銀行網站或網頁郵件),且該網站不是那種使用者會長時間保持登入狀態的類型時,Referrer 應該是來自該網站本身。任何其他的 Referrer 都應被視為可疑。然而,如果原始請求來自一個 HTTPS 頁面,那麼 referrer 將被移除,所以您不能依賴這個安全系統。
例如,http://vulnerable.example.com/ 可以採用以下安全檢查:
<syntaxhighlight lang="php"> if (strpos($_SERVER['HTTP_REFERER'], 'http://vulnerable.example.com/') !== 0) {
session_destroy(); // 銷毀會話中的所有資料
session_regenerate_id(); // 產生一個新的會話識別碼 </syntaxhighlight>
驗證額外資訊在整個會話中保持一致
進一步提高安全性的方法之一是確保使用者看起來是同一個終端使用者(用戶端)。這使得執行會話固定和其他攻擊變得更困難一些。
隨著越來越多的網路開始遵守 RFC 3704 和其他反欺騙實踐,IP 位址作為「相同來源」識別碼變得更加可靠。因此,透過驗證來源 IP 位址在整個會話中保持一致,可以提高網站的安全性。
這可以這樣執行:
<syntaxhighlight lang="php"> if ($_SERVER['REMOTE_ADDR'] != $_SESSION['PREV_REMOTEADDR']) {
session_destroy(); // 銷毀會話中的所有資料
session_regenerate_id(); // 產生一個新的會話識別碼 $_SESSION['PREV_REMOTEADDR'] = $_SERVER['REMOTE_ADDR']; </syntaxhighlight>
然而,在採用這種方法之前,需要考慮一些要點。
- 多個使用者可能共享一個 IP 位址。整個建築物使用 NAT 共享一個 IP 位址的情況並不少見。
- 一個使用者可能擁有不一致的 IP 位址。對於使用代理伺服器(如 AOL 客戶)的使用者來說是如此。對於一些行動/漫遊使用者,以及使用負載平衡網際網路連線的使用者也是如此。啟用 IPv6 隱私擴充功能的使用者也可能隨時更改他們的 IPv6 隱私位址。
- 對於雙協定堆疊用戶端,它無法可靠地運作,因為請求會在 IPv4 和 IPv6 之間切換。
- 對於行動使用者,它也無法可靠地運作,因為行動使用者也會在不同位址之間漫遊。
對於某些網站來說,增加的安全性勝過帶來的不便,而對於其他網站則不然。
User Agent
瀏覽器透過 "User-Agent" HTTP 標頭來識別自己。這個標頭通常在使用過程中不會改變;如果發生變化,那將是極其可疑的。一個網站應用程式可能會利用 User-Agent 偵測來試圖防止惡意使用者竊取會話。然而,這很容易繞過,因為攻擊者可以輕易地用自己的網站捕獲受害者的 user-agent,然後在攻擊中偽造它。這個提議的安全系統是依賴於隱晦式安全。
<syntaxhighlight lang="php"> if ($_SERVER['HTTP_USER_AGENT'] != $_SESSION['PREV_USERAGENT']) {
session_destroy(); // 銷毀會話中的所有資料
session_regenerate_id(); // 產生一個新的會話識別碼 $_SESSION['PREV_USERAGENT'] = $_SERVER['HTTP_USER_AGENT']; </syntaxhighlight>
然而,在採用這種方法之前,需要考慮一些要點。
- 在網咖中,多個使用者可能有相同的瀏覽器 User Agent。
- 多個使用者可能有相同的預設瀏覽器(例如:Windows XP SP3 中的 Internet Explorer 6 或手機中的迷你瀏覽器)。
但 User Agent 在少數情況下可能會合法地改變。以下範例是同一個使用者。
- 一部自上次請求後螢幕旋轉的智慧型手機
- Internet Explorer 相容模式:
- 一個使用者透過分佈在多個伺服器上的代理伺服器存取網站,而並非所有伺服器都已升級到最新版本的代理軟體
縱深防禦
縱深防禦是結合多種對策。其想法很簡單:如果一個障礙很容易克服,那麼多個障礙可能就非常難以克服。
縱深防禦策略可以包括:
- 啟用 HTTPS(以防範其他問題)
- 正確的設定(不接受外部 SID、設定逾時等)
- 執行 session_regeneration、支援登出等。
HTTP referrers 不會透過 SSL/TLS (HTTPS) 傳遞。
以下 PHP 腳本展示了以縱深防禦方式結合多種對策:
<syntaxhighlight lang="php"> if (isset($_GET['LOGOUT']) ||
$_SERVER['REMOTE_ADDR'] !== $_SESSION['PREV_REMOTEADDR'] ||
$_SERVER['HTTP_USER_AGENT'] !== $_SESSION['PREV_USERAGENT']) {
session_destroy();
session_regenerate_id(); // 產生一個新的會話識別碼
$_SESSION['PREV_USERAGENT'] = $_SERVER['HTTP_USER_AGENT']; $_SESSION['PREV_REMOTEADDR'] = $_SERVER['REMOTE_ADDR']; </syntaxhighlight> 請注意,此程式碼會檢查目前的 REMOTE_ADDR(使用者 IP 位址)和 User-agent,並與上一個請求的 REMOTE_ADDR 和 User-agent 進行比較。如上所述,這對某些網站來說可能不方便。
參見
- 會話毒化 (Session poisoning)
- 權限提升 (Privilege escalation)
參考資料
外部連結
- Security Corner: Session Fixation
- Session Fixation Vulnerability in Web-based Applications (PDF)
- Session Fixation Video example
- The Web Application Security Consortium Threat Classification
Category:網路安全漏洞利用