Sec-Fetch-*請求頭,瞭解下?
阿新 • • 發佈:2020-10-27
如果你使用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·架構組
福袋