登入後的小連結,卻涉及重要安全決定

顧客開啟已收藏的訂單頁,登入後自然希望回到該訂單。常見做法是把返回目的地隨登入要求傳送;然而查詢參數或表格欄位都可被訪客改動,亦可被人預先製作成貌似來自商戶的連結。如果伺服器照單全收並把瀏覽器送到指定網址,可信的登入頁便可能成為通往釣魚網站的跳板。OWASP 把這類情況列為未驗證轉址。業主需要先訂明哪些頁面確有返回需要,以及誰有權決定目的地,而非單靠網址看起來熟悉。

這個問題容易漏看,因為登入本身可能完全正常。測試員由首頁登入,不一定會試到被竄改或格式錯誤的返回值;只檢查驗證成功的自動測試,也可能忽略最後的 Location 回應。應把落點視作獨立的安全及易用性要求,列出需要延續的旅程,例如已儲存報價、預約或帳戶設定。還要事先決定頁面失效、屬於另一顧客,或需要不同角色時怎樣處理。目的地與商戶同網域,並不代表登入者獲准查看其中內容。

先決定目的地模型,再處理網址字串

較簡單的做法,是讓伺服器掌管目的地代號,而非由瀏覽器提供完整網址。例如要求只帶有「我的訂單」之類短代號,登入後再由伺服器對應到自家路由。若要返回某宗訂單的詳細頁,仍須在顯示頁面前核對訂單編號是否屬於該顧客。這樣可減少登入處理器需要理解的網址形式。OWASP 建議可行時使用伺服器端對應,同時提醒代號不能變成枚舉受保護資源的方法。目的地對應屬於設計選擇,不能取代目標頁本身的授權檢查。

有些服務確實需要保留本地路徑,例如返回已選篩選條件的搜尋結果。若所用框架提供維護中的本地網址檢查功能,應優先使用;微軟的 ASP.NET Core 指南便說明 IsLocalUrl 及 LocalRedirect 的用途。團隊仍須約定可接受的形狀,例如單一斜線開頭的路徑、獲批准的路由,以及預期內的查詢鍵。帶協定或主機的值、反斜線歧義及控制字元都不應憑肉眼放行。要以實際解析器和轉址功能的解讀為準。返回值不合格時,讓顧客去安全的帳戶首頁,登入仍可成功,避免合法用戶被錯誤參數卡住。

跨網站旅程須另作決定

合作夥伴可能要求顧客登入後轉到其網站,但這與「返回顧客原本要求的頁面」是兩項不同需求。不應只因一個合作流程需要跨網站目的地,就把本地返回規則全面放寬。先考慮顯示清楚標示的外站連結,讓顧客自行選擇離開。若業務上確需自動轉址,OWASP 建議明確列出獲批准的目的地,並比較解析後的協定、標準化主機、實際連接埠及必要的路徑限制。單靠字串前綴或包含指定字詞不足以驗證;網址權限部分的用戶資訊,也可能使惡意地址看似含有可信名稱。

返回目的地也應連結到產生它的實際旅程。假設顧客開始預約,期間在另一分頁登入不同帳戶,稍後才完成原來的登入;舊目的地縱使屬於本站,也可能令人意外。可按系統情況把意圖短暫保存在伺服器端工作階段,並定義逾時後的處理。完成驗證後必須重新核對存取權,而不能沿用登入前的假設。如果無法安全重建原來路由,應返回穩定的帳戶頁並提供明確的「繼續預約」入口,勝過默默把人送到無關頁面。這些產品規則值得工程及客服先行確認。

以訂單頁作實際推演

假設未登入顧客開啟某宗訂單詳情。系統先在伺服器端的待完成旅程記下訂單識別碼,再引導登入。驗證成功後,系統向訂單服務查核該帳戶能否讀取該訂單;可讀取時,才自行組成本站路由並轉入。若訂單已取消或不屬於該帳戶,則返回「我的訂單」並以普通文字說明。這個流程在合理情況下保留顧客原意,又不接收任意外站地址。若團隊改用本地返回路徑,也須同時通過解析、本地目的地、路由範圍及目標授權檢查,才可達到相同效果。

然後以同一流程測試經製作的連結:返回值指向無關網站、外觀相似的子網域、主機前含用戶資訊的網址,或以雙斜線開頭的形式。預期應到事先議定的安全落點,而非在 Location 標頭出現外站。亦要試編碼變體,以及路徑屬本站但訂單編號無權存取的情況。檢查伺服器回應及瀏覽器最終位置,不要只看登入畫面。正常電郵深層連結、已收藏頁面和已逾時返回值也要覆蓋。通過的標準,是合法旅程可以延續,而不可信目的地不能改寫落點。惡意樣本只應在受控測試環境使用。

讓規則可持續維護

把目的地模型與登入流程一同記錄,不要只留下一條無人理解的正規表達式。新增路由的負責人應知道它能否成為登入後落點,以及權限改變時怎樣處理。每次更改驗證或路由,都應回歸測試允許路由、拒絕路由、格式錯誤網址及外站來源。密碼重設、帳戶邀請及電郵驗證後的轉址,也要檢查是否共用相同處理。可以記錄被拒的目的地類別以便分析,但避免把可能含敏感資訊的完整查詢字串寫入日誌。拒絕數量突然上升值得查究;單一拒絕也可能只是舊書籤或壞掉的整合,不能自動判定為攻擊。

實際驗收問題很直接:未登入顧客完成登入後,能否到達原本想看的、而且獲准查看的頁面;同時,訪客可改動的值能否被阻止把任何人送往未批准的網站?兩邊都要示範。如果系統無法可靠判斷複雜返回網址是否安全,就收窄為伺服器掌管的目的地,特別的外站旅程提供手動連結。這可能多一次點擊,卻令產品行為容易向顧客及客服解釋。香港中小企常有中英雙語入口,兩種語言均應測試相同目的地規則;切換語言不應成為另一條檢查較鬆的轉址路徑。

決定失敗情況如何向顧客呈現

安全回退除了技術上拒絕,還需要顧客看得明白的說明。若預約連結已過期,落點頁應說明原來目的地不可用,並提供查看現有預約的方法。不要把被拒地址原樣顯示在頁面,亦不要製成「繼續」連結,否則會重建攻擊者的導流路徑。目的地被拒也不應清除已完成登入,令顧客反覆驗證。這些細節有助客服分辨逾期邀請與真正登入故障,無須要求顧客轉寄可能含敏感資訊的完整網址。回退文案應以兩種網站語言撰寫並分別測試。

若登入旅程跨越多個服務,須找出哪個組件最終發出轉址。前端路由器可能在伺服器驗證另一個值之後重新讀取返回參數;合作閘道亦可能加上自己的第二次轉址。由最初未登入的要求追到瀏覽器最終地址,記錄每層如何保存及轉換目的地。關鍵檢查應放在真正輸出轉址的組件之前,並使用它實際要送出的值。若後續層再解碼、拼接或取代字串,較早的驗證可能失效。這是設計審查工作,並非指稱任何特定框架本身有漏洞。

交接時可給團隊一張簡短決策表:獲准且有權存取的路由前往原頁;獲准但無權的路由前往帳戶頁;錯誤格式或外站目的地走安全回退;逾期待完成旅程請顧客重新開始。表格須列明每種結果由誰負責及顯示甚麼訊息。客服可據此重現個案,測試人員亦可核對實際 Location 標頭,而非只看最後有沒有出現某頁。網站日後增加語言或帳戶角色時,也能用同一張表有控制地擴展旅程,維持原有信任界線。

進行前先確認

  • 登入流程只接受已記錄的目的地形式。
  • 無權存取的本站頁面及未批准的外站均安全回退。
  • 正常深層連結在中英雙語旅程均可運作。

資料來源及延伸閱讀