防止 CSRF、XSSI 和跨來源資訊外洩。
為何要隔離網頁資源?
許多網頁應用程式都容易遭受跨來源攻擊,例如跨網站偽造要求 (CSRF)、跨網站指令碼置入 (XSSI)、時間攻擊、跨來源資訊外洩或推測執行旁路 (Spectre) 攻擊。
您可以使用「Fetch Metadata」要求標頭,部署強大的縱深防禦機制 (資源隔離政策),保護應用程式免於遭受這些常見的跨來源攻擊。
一般來說,特定網頁應用程式公開的資源通常只會由該應用程式載入,而不會由其他網站載入。在這種情況下,只要根據擷取中繼資料要求標頭部署資源隔離政策,即可輕鬆保護應用程式免於遭受跨網站攻擊。
瀏覽器相容性
所有新型瀏覽器引擎都支援擷取中繼資料要求標頭。
背景
網際網路預設為開放式,應用程式伺服器無法輕易保護自己免受來自外部應用程式的通訊攻擊,因此可能會發生許多跨網站攻擊。典型的跨來源攻擊是跨網站要求偽造 (CSRF),攻擊者會誘騙使用者前往攻擊者控制的網站,然後向使用者登入的伺服器提交表單。由於伺服器無法判斷要求是否來自其他網域 (跨網站),且瀏覽器會自動將 Cookie 附加至跨網站要求,因此伺服器會代表使用者執行攻擊者要求的動作。
其他跨網站攻擊 (例如跨網站指令碼加入 (XSSI) 或跨來源資訊外洩) 的性質與 CSRF 相似,都會在攻擊者控制的文件中載入受害應用程式的資源,並外洩受害應用程式的相關資訊。由於應用程式無法輕易區分可信與不可信的請求,因此無法捨棄惡意的跨網站流量。
隆重推出擷取中繼資料
擷取中繼資料要求標頭是新的網路平台安全性功能,旨在協助伺服器防範跨來源攻擊。透過在 Sec-Fetch-*
標頭組中提供 HTTP 要求內容的相關資訊,回應伺服器就能在處理要求前套用安全性政策。這樣一來,開發人員就能根據要求的提出方式和使用情境,決定是否接受或拒絕要求,進而只回應自有應用程式提出的合法要求。
Sec-Fetch-Site
Sec-Fetch-Site
會告訴伺服器哪個網站傳送了要求。瀏覽器會將這個值設為下列其中一個:
same-origin
,如果要求是由您自己的應用程式提出 (例如site.example
)same-site
,如果要求是由您網站的子網域提出 (例如bar.site.example
)none
,如果使用者與使用者代理程式互動 (例如點選書籤) 明確導致要求cross-site
,如果要求是由其他網站 (例如evil.example
) 傳送
Sec-Fetch-Mode
Sec-Fetch-Mode
表示要求的模式。這大致對應於要求類型,可讓您區分資源載入作業和導覽要求。舉例來說,navigate
目的地表示頂層導覽要求,而 no-cors
則表示資源要求,例如載入圖片。
Sec-Fetch-Dest
Sec-Fetch-Dest
會公開要求的目的地 (例如如果 script
或 img
標記導致瀏覽器要求資源)。
如何使用 Fetch 中繼資料防範跨來源攻擊
這些要求標頭提供的額外資訊相當簡單,但額外的背景資訊可讓您只需幾行程式碼,即可在伺服器端建立強大的安全性邏輯 (也稱為資源隔離政策)。
實作資源隔離政策
資源隔離政策可防止外部網站要求你的資源。封鎖這類流量有助於降低常見的跨網站網頁安全漏洞,例如 CSRF、XSSI、時間攻擊和跨來源資訊外洩。您可以為應用程式的所有端點啟用這項政策,這樣一來,系統就會允許來自您自己的應用程式以及直接導覽 (透過 HTTP GET
要求) 的所有資源要求。系統可選擇不套用這項邏輯,針對應在跨網站內容中載入的端點 (例如使用 CORS 載入的端點) 執行這項操作。
步驟 1:允許來自未傳送擷取中繼資料的瀏覽器的請求
由於並非所有瀏覽器都支援擷取中繼資料,因此您需要檢查是否有 sec-fetch-site
,藉此允許未設定 Sec-Fetch-*
標頭的要求。
if not req['sec-fetch-site']:
return True # Allow this request
步驟 2:允許同網站和瀏覽器啟動的請求
系統會允許所有未源自跨來源內容 (例如 evil.example
) 的要求。具體來說,這些要求必須符合以下條件:
- 來自您自己的應用程式 (例如,
site.example
要求site.example/foo.json
的相同來源要求一律會獲得許可)。 - 來自子網域。
- 明確由使用者與使用者代理程式互動所造成 (例如直接導覽或點選書籤等)。
if req['sec-fetch-site'] in ('same-origin', 'same-site', 'none'):
return True # Allow this request
步驟 3:允許簡單的頂層導覽和 iframing
為確保網站仍可從其他網站連結,您必須允許簡單 (HTTP GET
) 頂層導覽。
if req['sec-fetch-mode'] == 'navigate' and req.method == 'GET'
# <object> and <embed> send navigation requests, which we disallow.
and req['sec-fetch-dest'] not in ('object', 'embed'):
return True # Allow this request
步驟 4:停用用於跨網站流量的端點 (選用)
在某些情況下,您的應用程式可能會提供跨網站載入的資源。這些資源必須依據個別路徑或端點進行豁免。這類端點的例子包括:
- 可跨來源存取的端點:如果應用程式提供已啟用
CORS
的端點,您必須明確選擇不採用資源隔離功能,確保仍可對這些端點提出跨網站要求。 - 公開資源 (例如圖片、樣式等):任何公開且未經驗證的資源,只要應可從其他網站跨來源載入,也能豁免。
if req.path in ('/my_CORS_endpoint', '/favicon.png'):
return True
步驟 5:拒絕所有其他跨網站且非導覽的請求
這項資源隔離政策會拒絕任何其他跨網站要求,進而保護應用程式免受常見的跨網站攻擊。
範例:以下程式碼示範在伺服器上或以中介軟體形式實作完整的資源隔離政策,以便拒絕可能含有惡意的跨網站資源要求,同時允許簡單的導覽要求:
# Reject cross-origin requests to protect from CSRF, XSSI, and other bugs
def allow_request(req):
# Allow requests from browsers which don't send Fetch Metadata
if not req['sec-fetch-site']:
return True
# Allow same-site and browser-initiated requests
if req['sec-fetch-site'] in ('same-origin', 'same-site', 'none'):
return True
# Allow simple top-level navigations except <object> and <embed>
if req['sec-fetch-mode'] == 'navigate' and req.method == 'GET'
and req['sec-fetch-dest'] not in ('object', 'embed'):
return True
# [OPTIONAL] Exempt paths/endpoints meant to be served cross-origin.
if req.path in ('/my_CORS_endpoint', '/favicon.png'):
return True
# Reject all other requests that are cross-site and not navigational
return False
部署資源隔離政策
- 安裝上述程式碼片段等模組,記錄及監控網站的運作情形,並確保限制不會影響任何合法流量。
- 豁免合法的跨來源端點,修正潛在違規問題。
- 捨棄不符合規定的要求,以便強制執行政策。
找出並修正違反政策的行為
建議您先在伺服器端程式碼的報表模式中啟用政策,以免產生副作用。或者,您也可以在中介軟體或反向 Proxy 中實作這項邏輯,這樣系統在套用正式版流量時,就會記錄政策可能產生的任何違規情形。
根據我們在 Google 推出「擷取中繼資料資源隔離政策」的經驗,大多數應用程式預設為與這類政策相容,且很少需要豁免端點才能允許跨網站流量。
強制執行資源隔離政策
確認政策不會影響正式版流量後,您就可以開始實施限制,確保其他網站無法要求您的資源,並保護使用者免於遭受跨網站攻擊。
延伸閱讀
- W3C Fetch Metadata Request Headers 規格
- Fetch Metadata Playground
- Google I/O 演講:運用新穎平台功能保護網頁應用程式 (投影片)