若是你使用76+版本的chrome瀏覽器,經過開發者面板查看每一個網絡請求,會發現都有幾個Sec-Fetch開頭的請求頭,例如訪問百度首頁https://www.baidu.com/
的請求:git
Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1
這是用來幹嗎的呢,簡單來講,就是網絡請求的元數據描述,服務端根據這些補充數據進行細粒度的控制響應,換句話說,服務端能夠精確判斷請求的合法性,杜絕非法請求和攻擊,提升web服務的安全性。github
Fetch Metadata Request Headers
Sec-Fetch開頭的請求頭都屬於Fetch Metadata Request Headers,於2019年發佈的新草案,目前處於Editor's Draft階段,支持度還不是很高,還須要注意的是,這些請求頭都是Forbidden header,也就是不能被篡改的,是瀏覽器自動加上的請求頭,這樣也保證了數據的準確性,還須要注意的是若是資源是本地緩存加載,那麼就不會添加這些請求頭了,這也容易理解,就很少說了。web
規範的意義
近些年web領域發展迅速,可是安全問題也十分突出,從最初瀏覽器的同源模型到CSP,再到Fetch Metadata Request Headers,都是對web安全不斷的完善和增強,以往不少安全策略側重於客戶端的防禦,服務端須要識別非法請求每每比較困難,由於缺少判斷請求的依據,控制比較粗線條,而Fetch Metadata Request Headers的出現就爲服務端過濾非法請求提供了元數據,避免csrf,xssi等攻擊就很容易了。chrome
接下來探究一下這四個請求頭的含義;後端
Sec-Fetch-Dest
含義: 表示請求的目的地,即如何使用獲取的數據; 取值範圍:跨域
Destination | Features |
---|---|
report | CSP, NEL reports. |
document | HTML’s navigate algorithm (top-level only). |
frame | HTML’s <frame> |
iframe | HTML’s <iframe> |
- | navigator.sendBeacon() , EventSource , HTML’s <a ping=""> and <area ping=""> , fetch(), XMLHttpRequest , WebSocket , Cache API,HTML’s <link rel=prefetch>,HTML’s <link rel=prerender>,HTML’s download="" , "Save Link As…" UI |
object | HTML’s <object> |
embed | HTML’s <embed> |
audio | HTML’s <audio> |
font | CSS' @font-face |
image | HTML’s <img src> , /favicon.ico resource, SVG’s <image> , CSS' background-image , CSS' cursor, CSS' list-style-image , … |
audioworklet | audioWorklet.addModule() |
paintworklet | CSS.paintWorklet.addModule() |
script | HTML’s <script> , importScripts() |
serviceworker | navigator.serviceWorker.register() |
sharedworker | SharedWorker |
worker | HTML’s <link rel=stylesheet> , CSS' @import |
track | HTML’s <track> |
video | HTML’s <video> element |
image | HTML’s <img srcset> and <picture> |
manifest | HTML’s <link rel=manifest> |
xslt | <?xml-stylesheet> |
說明: Dest是destination的縮寫,根據上面的取值範圍可很容易理解了,這個請求頭指明客戶端請求的目的,指望須要什麼樣的資源;瀏覽器
Sec-Fetch-Mode
含義 該請求頭代表了一個請求的模式;緩存
取值範圍:安全
cors
:跨域請求;websocket
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類型的值,t
rue(?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