防止 CSRF、XSSI 和跨來源資訊外洩。
為何要隔離網頁資源?
許多網頁應用程式都容易遭受跨來源攻擊,例如跨網站偽造要求 (CSRF)、跨網站指令碼置入 (XSSI)、時間攻擊、跨來源資訊外洩或推測執行旁路 (Spectre) 攻擊。
擷取中繼資料要求標頭可讓您部署強大的縱深防禦機制 (資源隔離政策),以保護應用程式免受這些常見的跨來源攻擊。
一般來說,特定網頁應用程式公開的資源通常只會由該應用程式載入,而不會由其他網站載入。在這種情況下,只要根據擷取中繼資料要求標頭部署資源隔離政策,即可輕鬆保護應用程式免於遭受跨網站攻擊。
瀏覽器相容性
所有新式瀏覽器引擎都支援擷取中繼資料要求標頭。
背景
網頁預設為開放式,且應用程式伺服器無法輕易保護自己免受來自外部應用程式的通訊攻擊,因此可能會發生許多跨網站攻擊。典型的跨來源攻擊是跨網站要求偽造 (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
標記造成瀏覽器要求資源)。
如何使用擷取中繼資料來防範跨來源攻擊
這些要求標頭提供的額外資訊雖然簡單,但額外的內容可讓您在伺服器端建立強大的安全性邏輯,而且只需使用幾行程式碼即可 (也稱為資源隔離政策)。
實作資源隔離政策
資源隔離政策可防止外部網站要求你的資源。封鎖這類流量有助於降低常見的跨網站網頁安全漏洞,例如 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 實施「擷取中繼資料資源隔離政策」的經驗中,大部分的應用程式預設都與此類政策相容,而且很少要求豁免端點來允許跨網站流量。
強制執行資源隔離政策
確認政策不會影響正式版流量後,您就可以開始實施限制,確保其他網站無法要求您的資源,並保護使用者免於遭受跨網站攻擊。