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

若是你使用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-Sitesame-origin;

2.https://example.com/redirect重定向到https://subdomain.example.com/redirect,此時的Sec-Fetch-Sitesame-site (由於是一級請求二級域名);

3.https://subdomain.example.com/redirect重定向到https://example.net/redirect,此時的Sec-Fetch-Sitecross-site (由於https://example.net/https://example.com&https://subdomain.example.com/是不一樣站點);

4.https://example.net/redirect重定向到https://example.com/,此時的Sec-Fetch-Sitecross-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-sitesame-origin, same-site, none三種之一的請求;

3.允許sec-fetch-modenavigate且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

相關文章
相關標籤/搜索