有一天在開發頁面的時候,為了要檢查請求 Header 的欄位是否正確,打開開發工具一看,發現了請求多出了幾個可疑的 header:
有注意到可疑的地方嗎?仔細一看發現,怎麼有三個以 Sec-Fetch-*
開頭的標頭。
這引起了我的注意,我並沒有設定這些標頭,也沒有套用其他函式,為什麼會自己幫我加上這些標頭,猜測應該是瀏覽器在作祟。
查了一下 google 發現,原來這是一個新的草案,叫做 Fetch Metadata Request Headers。目前只有 chrome 才會加上這些標頭,Firefox, Safari 並不會。
再談同源政策(CORS)
我們都知道,瀏覽器為了防止一些有心人士用奇怪的手法竊取你的資料,限制了某些來源的存取條件,例如跨域請求時需要加上 Access-Control-Allow-Origin
或是 Preflight 請求等等,詳細的內容可以參考我之前寫的「如何和 CORS 與 Cookie 打交道」。
儘管如此還是有些缺陷,像是 <img/>
仍然可以送出跨域請求,這樣有心人士就可以略過 CORS 的限制。當然,如果伺服器有做好基本的防護措施,還是可以擋下大部分不安全的請求。
如果可以再送出請求的時候,加上一些額外的標頭資訊(metadata)描述這個請求是從哪裡來的話,伺服器也可以根據標頭來做適當的回應,於是誕生出這樣的草案。
標頭規格:
目前主要有四個標頭在草案當中:
1. Sec-Fetch-Dest
代表這個請求的目的地是哪裡。
可能的值有 audio、document、font、image、object、serviceworker 等等。這樣有幾個好處,有了這些 header 的判斷,伺服器馬上就可以知道這個請求來源是否合法,例如如果這個來源是從 <img>
來的,卻不是跟伺服器要圖片,那麼十之八九是駭客,我們就可以直接回應錯誤給他。
2. Sec-Fetch-Mode
代表請求的模式。主要有 cors、navigate、nested-navigate、no-cors 等等,來判斷這個請求的模式是什麼,類似 fetch
當中的 mode。
像是 Set-Fetch-User
我們也可以知道使用者是否是透過操作(例如點擊、鍵盤等等)來發出請求的。
3. Sec-Fetch-Site
代表請求的來源是同源還是跨域。
4. Sec-Fetch-User
這個標頭的值是布林值,只有在請求是只有在 navigation request(request 的目的地為 document)而且有互動時(例如按下按鈕、鍵盤等等)才會是 true,伺服器可以根據這個標頭來判斷使用者觸發請求的方式是否合法。
像是點擊 form 表單送出的時候,因為是由 document 送出請求,且使用者有互動(點擊按鈕送出),所以 Set-Fetch-User
會是 ?T
。
有辦法把標頭拿掉嗎?
如果成為規範之一的話,可能沒有辦法把它拿掉。另外你不可以透過手動設定來改寫標頭的值,因為它屬於 forbidden headers 之一。
結論
以往要做到類似的事情,都會用自定義的 header 來防範,但還是有可能被偽造,而且加上自定義的標頭就需要做 prelight 的檢查,又多了一份工。從規範下手的好處在於這些標頭沒辦法被輕易改寫,而且提供了統一的標準,有了這些標頭,就可以讓後端在收到請求時多做一些判斷,進而提高安全性。