1. 程式人生 > >Sec-Fetch-*請求頭,瞭解下?

Sec-Fetch-*請求頭,瞭解下?

如果你使用76+版本的chrome瀏覽器,通過開發者面板檢視每個網路請求,會發現都有幾個Sec-Fetch開頭的請求頭,例如訪問百度首頁`https://www.baidu.com/`的請求: ``` Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1 ``` 這是用來幹嘛的呢,簡單來說,就是網路請求的元資料描述,服務端根據這些補充資料進行細粒度的控制響應,換句話說,服務端可以精確判斷請求的合法性,杜絕非法請求和攻擊,提高web服務的安全性。 ### Fetch Metadata Request Headers Sec-Fetch開頭的請求頭都屬於Fetch Metadata Request Headers,於2019年釋出的新草案,目前處於Editor's Draft階段,支援度還不是很高,還需要注意的是,這些請求頭都是Forbidden header,也就是不能被篡改的,是瀏覽器自動加上的請求頭,這樣也保證了資料的準確性,還需要注意的是如果資源是本地快取載入,那麼就不會新增這些請求頭了,這也容易理解,就不多說了。 ### 規範的意義 近些年web領域發展迅速,但是安全問題也十分突出,從最初瀏覽器的同源模型到CSP,再到Fetch Metadata Request Headers,都是對web安全不斷的完善和加強,以往很多安全策略側重於客戶端的防護,服務端需要識別非法請求往往比較困難,因為缺乏判斷請求的依據,控制比較粗線條,而Fetch Metadata Request Headers的出現就為服務端過濾非法請求提供了元資料,避免csrf,xssi等攻擊就很容易了。 接下來探究一下這四個請求頭的含義; ### Sec-Fetch-Dest __含義:__ 表示請求的目的地,即如何使用獲取的資料; __取值範圍:__ ![圖片描述](https://fulu-common-util.oss-cn-hangzhou.aliyuncs.com/wiki_assets/sec/sec-fetch-dest.jpg) __說明:__ Dest是destination的縮寫,根據上面的取值範圍可很容易理解了,這個請求頭指明客戶端請求的目的,期望需要什麼樣的資源; ### Sec-Fetch-Mode __含義__ 該請求頭表明了一個請求的模式; __取值範圍:__ `cors`:跨域請求; `no-cors`:限制請求只能使用請求方法(get/post/put)和請求頭(accept/accept-language/content-language/content-type); `same-origin`:如果使用此模式向另外一個源傳送請求,顯而易見,結果會是一個錯誤。你可以設定該模式以確保請求總是向當前的源發起的; `navigate`:表示這是一個瀏覽器的頁面切換請求(request)。 navigate請求僅在瀏覽器切換頁面時建立,該請求應該返回HTML; `websocket`:建立websocket連線; __說明:__ cors表示跨域請求,且要求後端需要設定cors響應頭;no-cors並不是代表請求不跨域,而是服務端不設定cors響應頭,什麼情況下會是這種模式呢,圖片/指令碼/樣式表這些請求是容許跨域且不用設定跨域響應頭的,而no-cors也是預設的模式;same-origin表示同源請求,這就限制了不能跨域,前面說的cors和no-cors是容許跨域的,只是要求服務端的設定不同而已,熟悉fetch介面的同學對mode屬性應該不陌生,其實跟這裡的含義是一樣的,只是fetch的mode大家可以手動設定,而Sec-Fetch-Mode不能干預而已; ### Sec-Fetch-Site __含義:__ 表示一個請求發起者的來源與目標資源來源之間的關係; __取值範圍:__ `cross-site`:跨域請求; `same-origin`:發起和目標站點源完全一致; `same-site`:有幾種判定情況,詳見說明; `none`:如果使用者直接觸發頁面導航,例如在瀏覽器位址列中輸入地址,點選書籤跳轉等,就會設定none; __說明:__ same-site有幾種情況(A->B): |A|B|same site| |:--|:--|:--| |(" `https` ", " `example.com` ")|(" `https` ", " `sub.example.com` ")|true | |(" `https` ", " `example.com` ")|(" `https` ", " `sub.other.example.com` ")|true| |(" `https` ", " `example.com` ")|(" `http` ", " `non-secure.example.com` ")|false| |(" `https` ", " `r.wildlife.museum` ")|(" `https` ", " `sub.r.wildlife.museum` ")|true| |(" `https` ", " `r.wildlife.museum` ")|(" `https` ", " `sub.other.r.wildlife.museum` ")|true| | (" `https` ", " `r.wildlife.museum` ")|(" `https` ", " `other.wildlife.museum` ")|false| |(" `https` ", " `r.wildlife.museum` ")|(" `https` ", " `wildlife.museum` ")|false| |(" `https` ", " `wildlife.museum` ")|(" `https` ", " `wildlife.museum` ")|true| 在地址有重定向的情況下,Sec-Fetch-Site取值稍微複雜一點,直接參考一下示例: 1.`https://example.com/` 請求`https://example.com/redirect`,此時的`Sec-Fetch-Site` 是`same-origin`; 2.`https://example.com/redirect`重定向到`https://subdomain.example.com/redirect`,此時的`Sec-Fetch-Site` 是`same-site` (因為是一級請求二級域名); 3.`https://subdomain.example.com/redirect`重定向到`https://example.net/redirect`,此時的`Sec-Fetch-Site` 是`cross-site` (因為`https://example.net/`和`https://example.com`&`https://subdomain.example.com/`是不同站點); 4.`https://example.net/redirect`重定向到`https://example.com/`,此時的`Sec-Fetch-Site` 是`cross-site`(因為重定向地址鏈裡包含了`https://example.net/`); ### Sec-Fetch-User __含義:__ 取值是一個Boolean型別的值,true(?1)表示導航請求由使用者啟用觸發(滑鼠點選/鍵盤),false(?0)表示導航請求由使用者啟用以外的原因觸發; __取值範圍:__ `?0` `?1` __說明:__ 請求頭只會在導航請求情況下攜帶,導航請求包括`document` , `embed` , `frame` , `iframe` , or `object` ; ### 安全策略 瞭解了上面是個請求頭的含義之後,我們就可以根據專案實際情況來制定安全策略了,例如google I/O提供的一個示例: ``` # Reject cross-origin requests to protect from CSRF, XSSI & 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 from anywhere if req['sec-fetch-mode'] == 'navigate' and req.method == 'GET': return True return False ``` 1.瀏覽器不支援Sec-Fetch-*請求頭,則不做處理; 2.容許`sec-fetch-site`為`same-origin`, `same-site`, `none`三種之一的請求; 3.容許`sec-fetch-mode`為`navigate`且get請求的方法; 4.容許部分跨域請求,可設定白名單進行匹配; 5.禁止其他非導航的跨域請求,確保由使用者直接發起; 在使用Fetch Metadata Request Headers時,還需要注意Vary響應頭的正確設定,Vary這個響應頭是幹嘛的呢,其實就是快取的版本控制,當客戶端請求頭中的值包含在Vary中時,就會去匹配對應的快取版本(如果失效就會同步資源),因此針對不同的請求,能提供不同的快取資料,可以理解為差異化服務,說明白了Vary響應頭之後,就明白了Fetch Metadata Request Headers與Vary的影響關係了,因為要確保快取能正確處理攜帶Sec-Fetch-*請求頭的客戶端響應,例如`Vary: Accept-Encoding, Sec-Fetch-Site`,因此有沒有攜帶Sec-Fetch-Site將會對應兩個快取版本。 ### 參考資料: https://developer.mozilla.org/zh-CN/docs/Web/API/Request/mode https://fetch.spec.whatwg.org/#concept-request-mode https://web.dev/fetch-metadata/ https://w3c.github.io/webappsec-fetch-metadata/#intro 福祿ICH·架構組 福袋