題目與適用情境
請為一個 URL 預覽 API 設計安全擷取路徑。已登入使用者可以提交任意公網 HTTP 或 HTTPS URL, 背景工作擷取文件並只回傳經過清理的頁面標題。產品的輸入就是公開網站,因此不能維護一份固定網域 允許清單。
本題只允許預設的 80 和 443 連接埠,最多跟隨 3 次重新導向,整個工作總時限為 3 秒,解壓縮後最多 讀取 2 MiB。這些數字是面試假設,不是通用安全標準。服務不得透過 IPv4 或 IPv6 存取回環、私有 網路、共享、鏈路本地、群播、保留、文件或雲端中繼資料目標;還要應對特殊 IP 寫法、混合 DNS 結果、 DNS rebinding、公網 URL 重新導向內網、超大回應和慢速伺服器。
MITRE 對 CWE-918 的定義是:伺服器收到 URL 並發出請求,卻沒有充分確認請求是否到達預期目標。 這個定義點明了題目的核心——出站授權。輸入驗證、DNS 行為、網路路由和最終 socket 目的位址必須 執行同一套策略。一則 2026 年公開 Web 安全面試討論把 SSRF 與 XSS、SQL injection、IDOR 一起列為 候選人應準備說明的主題。這能證明題目仍有當前面試情境,不能據此聲稱某家公司固定出題或頻繁出現。 OWASP Top 10:2025 已把 CWE-918 納入 Broken Access Control,也支持用授權問題理解 SSRF。
既有題庫在爬蟲和短網址系統中各用一小段提到 SSRF。本題單獨考察擷取路徑:URL 解析、DNS 解析、 目的位址分類、連線綁定、重新導向、出站控制,以及如何證明策略不會被檢查時與使用時之間的競態繞過。
面試官考察什麼
第一項是候選人能否把目的位址當成授權決策。只阻擋字串 localhost 和 127.0.0.1 並不完整。 目標可能是 IPv6 字面值、IPv4-mapped IPv6、其他數字寫法、同時回傳安全和危險位址的網域、重新 導向目標,或是在驗證與連線之間改變 DNS 結果的網域。
第二項是解析器紀律。正規表示式和子字串比對不能定義 URL 語意。高品質回答會統一使用經過驗證的 標準解析器,拒絕解析失敗和內嵌認證資訊,只允許明確的 HTTP 協定與連接埠,而且後續所有決策都取自 解析器的規範欄位。即使 socket 綁定到已批准的 IP,TLS 仍應使用原始規範網域做 SNI 和憑證主機名 驗證。
第三項是能否封住 DNS rebinding 的空窗。先解析並批准一個 IP,接著 HTTP 用戶端在建立連線前再次 查詢 DNS,這次驗證就沒有意義。擷取器必須連線到指定的已批准位址,或交給能完成相同動作的策略化 出站代理。每次重新導向和重試都會產生一次新的授權決策。
第四項是縱深防禦。應用層即使出現遺漏,網路層也應無法路由到內網和中繼資料位址。工作行程使用最小 權限身分,不攜帶使用者 Cookie 或授權標頭;解析過程有資源上限,也不會執行頁面 script 或載入 子資源。
最後一項是可證偽的驗證。測試必須涵蓋特殊位址寫法、混合 A/AAAA 記錄、二次解析競態、重新導向、 解壓縮上限和出站防火牆。籠統地說「加允許清單」不符合本題,因為任意公網網域就是產品需求;允許 清單適合另一個只接入固定合作方的情境。
回答前要確認的問題
- 目標是任意公網網站還是固定合作方? 固定合作方可以使用精確的協定、網域和連接埠允許清單。
任意 URL 預覽需要保守的公網位址策略,以及由網路層強制執行的出站邊界。
- 需要哪些協定、連接埠、方法和請求標頭? 本題只允許 HTTP 使用 80 連接埠、HTTPS 使用 443
連接埠發送 GET,不接受呼叫方指定方法、請求內容、代理、Cookie、授權標頭或任意標頭。
- 是否需要跟隨重新導向? 需要,最多 3 跳。關閉自動跟隨,每個
Location目標都獨立授權後
再存取。
- 最後需要什麼輸出? 只需要清理後的標題。工作行程不回傳原始 HTML,不執行 JavaScript,不
解析 XML 外部實體,也不擷取圖片、樣式表、字型或 script。
- 工作行程能路由到哪些位址? 回答要涵蓋 IPv4、IPv6、容器與服務網路、公司內網和雲端供應商
中繼資料位址。先盤點有效路由,才能驗證出站策略。
- 快取和重試如何處理? 快取可減少重複擷取,但未命中仍走完整安全路徑。本設計預設不自動重試;
若明確重試,必須從 DNS 解析、授權和連線綁定重新開始。
- 可用性如何取捨? 如果網域回傳的任一位址不安全,本題拒絕整個目標。這可能擋住設定錯誤的公開
網站,但策略會穩定地失敗關閉,不會悄悄改用另一個解析結果。
30 秒回答框架
「我會用標準 URL 解析器,限定 HTTP 使用 80 連接埠、HTTPS 使用 443 連接埠,並拒絕內嵌認證資訊。 網域透過受控解析器查詢全部 A 和 AAAA 結果;IPv4-mapped IPv6 先還原,只要一個位址不符合持續 維護的公網策略就拒絕目標。工作行程直接連線已批准 IP,但保留原網域做 Host、TLS SNI 和憑證驗證, 封住 DNS rebinding 空窗。關閉自動重新導向和重試,每一跳都重做完整檢查;隔離的出站防火牆再阻止 內網和中繼資料路由。最後落實 3 秒、2 MiB、3 跳上限,不載入子資源,並端對端測試 rebinding 和 重新導向中繼資料位址。」
分步深入分析
第一步:把需求寫成一套目的位址策略
把規則集中表達,避免 API、工作行程和 HTTP 用戶端各寫一套字串判斷:
DestinationPolicy
schemes: http, https
origin_pairs: http:80, https:443
userinfo: forbidden
address_requirement: globally reachable public unicast
redirects: at most 3, authorize every hop
method: GET
caller_headers: none
total_deadline: 3 seconds
decompressed_body_limit: 2 MiBAPI 負責驗證呼叫方、執行帳戶和租戶額度、把原始 URL 當成不可信資料保存並投遞工作。不要在 API 做一次「安全驗證」後把可信標記傳給稍後執行的工作行程;工作執行前 DNS 和路由都可能變化。真正打開 連線的是工作行程,所以它必須在建立連線前做權威決策。
固定合作方的最強策略是精確列出規範協定、網域和連接埠,必要時再限制路徑。本題必須存取任意公網 主機,所以只允許被分類為公網且全球可達的目的位址。分類資料應來自權威位址註冊表和組織自己的網路 清單。手寫一份只有 RFC 1918 的清單會漏掉回環、鏈路本地、共享、群播、保留、文件、IPv6 唯一本地、 IPv4-mapped 形式以及雲端供應商特定的中繼資料位址。
第二步:先解析 URL,後續只使用解析欄位
使用平台的 WHATWG 相容解析器或同等成熟的 URL 解析器,並要求絕對 URL。解析失敗、帶使用者名稱或 密碼、產品不需要的 fragment、非 HTTP 協定以及策略外連接埠都直接拒絕。國際化網域也交給解析器 規範化,後續不能再用正規表示式重新解讀原始字串。
https://expected.example@evil.example/ 說明了前綴比對的錯誤:真正建立連線的主機是 evil.example。fragment 不決定網路目標,編碼和特殊數字形式也可能讓驗證器與 HTTP 用戶端產生 不同理解。協定、規範主機、有效連接埠和請求路徑必須來自同一個解析結果。
如果主機本身就是 IP 字面值,立即規範化並分類。IPv4-mapped IPv6 要先取出內嵌 IPv4,再同時套用 兩類位址策略。不能根據標點、字串長度或「看起來像網域」來判斷安全性。
第三步:驗證全部解析位址,並把連線綁定到批准結果
網域透過受控解析器查詢 A 與 AAAA 記錄,依正常 DNS 過程處理 CNAME,並收集最終位址。本題只要 出現一個策略禁止的結果,就拒絕整個目標。每個位址和 IP 字面值使用同一分類器。拒絕時記錄原因碼, 不要記錄可能帶查詢秘密的完整 URL。
核心不變量是:
策略批准的 IP == connect() 實際使用的 IP從已批准結果選擇一個 IP,並把這個確切位址交給 socket 層。HTTP Host 仍使用規範網域;HTTPS 的 TLS SNI 和憑證主機名驗證也使用網域,不能用一張只對數字 IP 有效的憑證取代。禁止 HTTP 用戶端自行 再次解析 DNS,否則攻擊者可以在驗證時回傳公網位址,在建立連線時回傳私有網路位址。
也可以讓策略化出站代理統一負責解析、分類和連線綁定。工作行程把規範 URL 與固定請求策略交給代理, 不能接受攻擊者指定的代理位址。做授權決策與打開 socket 的必須是同一元件,或透過嚴格受控邊界傳遞 不可竄改的已批准位址結果。
DNS 正常情況也會輪換位址,所以綁定只涵蓋目前一跳,不應永久快取。後續工作、重新導向或明確重試 都重新解析和授權。連線池要按已授權 origin 與策略分組,不能因為兩個不可信 URL 字串相似就重用連線。
第四步:把每次重新導向都當成新的出站請求
關閉 HTTP 用戶端的自動重新導向。收到重新導向回應後,使用同一解析器把 Location 與目前 URL 合併,增加跳數,再重新執行協定、連接埠、userinfo、DNS、位址和連線檢查。缺失或格式錯誤的目標、 循環、第 4 跳以及任何落入禁止位址的重新導向都拒絕。
第一跳不得轉送呼叫方 Cookie、授權標頭或任意標頭,跨 origin 也不能攜帶回應建立的認證資訊。擷取器只 使用固定 User-Agent 和少量固定標頭。本題只擷取標題,GET 足夠;重新導向不能把操作變成呼叫方可 控制請求內容的 POST。
這能封住常見繞過:攻擊者控制的公網 URL 回傳到 http://169.254.169.254/、 http://127.0.0.1/ 或內網服務的重新導向。只批准第一條 URL,最後存取的就是未經授權的另一個目標。
第五步:讓網路層擋住應用層遺漏
擷取工作行程執行在專用網路區段或 namespace。出站防火牆只允許向受控解析器發送 DNS,而且只允許 HTTP/HTTPS 透過策略代理或批准的公網路徑。對 IPv4 與 IPv6 都阻止回環逃逸、內部服務區段、叢集 網路、公司內網、鏈路本地和雲端中繼資料位址。驗證實際生效的路由,不能只檢查規則文字。
工作行程的服務身分只允許讀取和完成預覽工作,環境中不放廣泛的雲端權限。在 EC2 上,不使用執行個體 中繼資料時可以關閉該服務,或要求 IMDSv2。AWS 文件同時列出了 IPv4 中繼資料端點 169.254.169.254 和可選 IPv6 端點。這些措施提供縱深防禦,不能取代 URL 目的位址策略。
預覽工作應使用獨立工作池,不能重用可存取內部 webhook 或管理介面的通用 HTTP 用戶端。網路拒絕的 存取,包括中繼資料目標,要產生指標與告警,但不向呼叫方暴露內部拓撲。
第六步:限制擷取成本,只解析承諾回傳的內容
分別設定 DNS、連線、首位元組、閒置讀取和 3 秒總時限。串流讀取回應,解壓縮後達到 2 MiB 就停止; 只限制壓縮內容大小仍可能遭受解壓縮炸彈。只接受擷取 HTML 標題所需的內容類型,並按帳戶和全域限制 並行,避免大量慢速伺服器占滿工作行程。
預設不自動重試,因為重試就是另一次出站授權,也會放大負載。以後若產品需要重試,每次嘗試都必須 從 URL 和 DNS 解析開始,並且仍受整個工作預算約束。
解析器把回應內容視為惡意輸入。它不執行 JavaScript,不解析 XML 外部實體,不載入圖片、樣式表、 字型、iframe 或 script,也不跟隨頁面中的 meta refresh。用有資源上限的串流或隔離解析器擷取標題, 把它規範為文字並限制長度,只回傳這個值。API 不回傳原始 HTML,最後顯示標題時還要在輸出位置正確 跳脫。
第七步:觀測決策,並驗證 socket 層不變量
記錄工作結果、規範主機或保護隱私的主機鍵、協定、有效連接埠、選中位址類別、重新導向數、位元組數、 耗時和穩定拒絕原因。完整 URL、查詢字串、userinfo、回應內容和 DNS 內容可能含有秘密,不應進入 記錄檔。分別統計私有網路、鏈路本地、中繼資料、協定、連接埠、重新導向、大小、逾時和內容類型拒絕率。
測試必須經過真實解析器、代理、防火牆和 HTTP 用戶端,包括:
127.1、IPv6::1、IPv4-mapped IPv6 等回環變體;- 私有網路、共享、鏈路本地、群播、保留、文件和中繼資料位址;
- 同時回傳一個公網和一個私有網路位址的網域,以及同時存在 A、AAAA 的情況;
- 驗證時回傳公網、下一次查詢改為私有網路的解析器;
- 重新導向各種禁止位址的公網端點,以及產生 4 次循環重新導向的端點;
- 內嵌認證資訊、編碼主機、非 HTTP 協定、禁止連接埠和格式錯誤 URL;
- 慢回應標頭、停滯回應內容、解壓縮後超限內容和解壓縮炸彈;
- script 或圖片指向內網主機的 HTML 頁面;
- socket 綁定指定 IP、TLS 仍驗證原網域的合法公網 HTTPS 頁面;
- 故意繞過應用策略,但仍必須被出站防火牆阻止的請求。
如果用戶端二次解析 DNS,rebinding 測試就必須失敗;如果憑證誤按數字 IP 驗證,正向 TLS 測試也 必須失敗。兩項一起證明驗證和連線使用的是預期身分。
高品質示範回答
「我會把 SSRF 防護定義為由建立連線元件執行的出站授權。API 只負責驗證身分和限流,把 URL 當成 不可信輸入保存並投遞工作。工作行程使用標準解析器,拒絕 userinfo、非 HTTP 協定以及 80、443 以外的連接埠,後續只使用解析器給出的規範主機和路徑。
IP 字面值先規範化,IPv4-mapped IPv6 先還原,再通過持續維護的公網位址分類器。網域由受控解析器 查詢全部 A 和 AAAA 結果;其中任一結果屬於私有網路、回環、共享、鏈路本地、群播、保留、文件、 中繼資料或策略外範圍,就拒絕整個目標。接著直接連線一個已批准 IP,同時保留原網域用於 Host、 TLS SNI 和憑證驗證,消除 DNS rebinding 利用的二次解析空窗。
關閉自動重新導向和重試。最多 3 跳,每一跳都重新解析目標並完成整套授權後再建立連線。只發送固定 標頭的 GET,不攜帶呼叫方 Cookie 或憑證。專用出站代理或防火牆再獨立禁止 IPv4、IPv6 的內網、 叢集、公司網路、鏈路本地和中繼資料路由;工作行程只有最小權限,也不能使用通用內網 HTTP 用戶端。
每項工作總時限 3 秒,解壓縮後回應上限 2 MiB。解析器不執行 script、不載入子資源,只回傳限制 長度並清理後的標題。記錄檔記錄原因碼、位址類別、重新導向數、位元組和延遲,不記錄完整 URL。驗收 涵蓋混合 DNS 結果、驗證與建立連線間 rebinding、IPv4-mapped IPv6、重新導向中繼資料位址、解壓縮 炸彈、慢回應內容、綁定 IP 後的 TLS,以及必須被網路層兜底阻止的應用策略繞過。」
常見錯誤
- 只阻擋
localhost和 RFC 1918 → 特殊 IPv4、IPv6、鏈路本地、共享、保留和中繼資料位址
仍可到達 → 規範化兩類位址,並使用持續維護的公網目的位址策略分類。
- 用正規表示式驗證 URL → 驗證器和 HTTP 解析器可能對認證資訊、編碼、主機和連接埠產生不同理解 →
統一使用標準解析器,而且只使用它的規範欄位。
- 解析並驗證後讓用戶端再次解析 → DNS rebinding 能在批准後改變 socket 目標 → **把連線綁定到
已批准 IP,同時用原網域完成 TLS 驗證。**
- 只批准第一條 URL → 合法公網主機可以重新導向內網 → 關閉自動重新導向,逐跳重新授權。
- 從混合 DNS 結果中挑一個安全位址 → 位址選擇變化後可能存取危險結果 → **本題只要有一個回傳
位址違規,就拒絕整個網域。**
- 轉送呼叫方標頭 → Cookie 或授權值會洩漏給攻擊者指定的主機 → **建立不攜帶環境認證資訊的固定
出站請求。**
- 只依賴 IMDSv2 或雲端設定 → 其他內部服務與位址區段仍然暴露 → **組合應用授權、最小權限、
中繼資料加固和出站阻擋。**
- 只限制壓縮前位元組 → 很小的回應也可能解壓縮到耗盡記憶體或 CPU → **同時限制解壓縮後位元組、
解析工作、時間和並行。**
- 像瀏覽器一樣解析頁面 → script 和子資源會發出未經審查的新請求 → **只擷取承諾的內容,並關閉
活動內容與外部實體。**
- 為了除錯記錄完整 URL → 查詢秘密和憑證進入可觀測系統 → 只記錄受限的規範欄位和穩定原因碼。
追問
所有目標都是固定合作方時,設計有什麼變化?
使用經過審查設定的精確協定、規範網域和連接埠允許清單。即使網域可信,也要解析和分類位址,避免 DNS 記錄被攻破或設定錯誤後指向內網。確實需要存取合作方私有端點時,應建立獨立的已驗證整合和明確 網路路徑,不能放進公網預覽工作行程。
能否安全支援跨網域重新導向?
如果產品需要,可以,但每一跳都必須重複完整策略。移除認證資訊和 origin 狀態,解析新網域,檢查全部 位址,綁定新連線並累計跳數。更簡單的策略是只允許同 origin 重新導向,但會擋住常見公網重新導向 服務;回答時應明確這個產品取捨。
為什麼一個 DNS 結果是公網、另一個是私有網路時要全部拒絕?
這樣能形成穩定的失敗關閉契約。不同解析器、IP 協定族和重試可能改變位址順序;驗證元件挑公網位址, 後續元件卻挑到私有網路位址,就會重新打開漏洞。若產品一定要部分接受,必須保證同一元件在每次連線 中只綁定批准位址,並仔細測試容錯移轉;本題選擇更簡單保守的策略。
產品需要截圖或執行 JavaScript 的預覽怎麼辦?
把渲染放到隔離程度更高的獨立層,並沿用同一出站策略。瀏覽器會產生大量次要請求,所以每次導覽、 重新導向、worker、WebSocket 和子資源都要攔截授權。關閉本機檔案存取和無關瀏覽器能力,限制 CPU、 記憶體、時間與下載量,每個工作結束後銷毀沙箱。初始頁面 URL 不能自動給後續請求授予信任。
部署後如何證明防火牆真的生效?
從真實工作行程身分和網路 namespace 執行受控探針,存取應被阻止的 IPv4、IPv6、叢集、公司內網和 中繼資料目標,確認連線失敗且網路遙測出現預期記錄。路由、容器、代理或雲端網路變更後重複執行。 設定審查只能證明意圖,探針和 flow logs 才能證明實際路徑。