你不能不知道的安全性 HTTP headers

Larry Lu
Starbugs Weekly 星巴哥技術專欄
8 min readAug 8, 2021

--

隨著網路上的 Web 應用程式越來越多,為了提升安全性,現在跟安全性有關的 HTTP header 也是多到記不得,像我上 Medium 看我自己的文章,就可以看到 CSP、HSTS、Expect-CT 等等 header

因為各種不同功能的 HTTP header 實在太多,所以這邊想要介紹幾個比較簡單、好設定的安全性 headers ,只要把這些 headers 加進去,網站就會突然變安全哦~

Content Security Policy (CSP)

首先是歷史悠久的 CSP,這個 header 是用來限制瀏覽器只能從哪些地方載入資源。譬如說我設定 Content-Security-Policy: default-src larry.com,就代表我限制瀏覽器只能從 larry.com 這個網域載入圖片、CSS、字體等等各種資源

當然想設定很多個網域也是可以的,只要一直往下接就可以了。下圖開發者工具的內容是我上 Github 首頁時 response header 裡的 CSP 設定,他就是把 uploads.github.comapi.github.com 等等很多個網域都加進去,雖然看起來很冗長,但至少有一個白名單,不會莫名其妙載入其他網域的東西

如果想針對不同類型的資源有不同的 policy(雖然比較麻煩但會更安全),那也可以寫成 Content-Security-Policy: img-src a.com; font-src b.com 的形式,那瀏覽器就會知道你希望只能從 a.com 載入圖片、從 b.com 載入字體

想看還有哪些屬性可以設定可以參考 MDN 上的 Directives

那限制網域有什麼用呢?假如哪天駭客在我的網站上發現了一個 XSS 漏洞,讓他可以在首頁的 HTML 中加入一段 <script src=“hacker.com/evil.js”>,那每一個使用者到我的網站時瀏覽器就會載入惡意的 evil.js,讓駭客可以做一些壞壞的事情,譬如說重新導向、偷走帳號密碼等等

但如果有把 CSP 設定成 Content-Security-Policy: default-src larry.com 的話,瀏覽器就會拒絕載入 evil.js(下圖),因為那個腳本是從 hacker.com 來的。因此雖然 CSP 不能完全防堵 XSS,但能減輕 XSS 造成的影響,因為如果攻擊者不能從外部載入惡意資源,那可以做的事情也相對比較少一點

Strict Transport Security (HSTS)

HSTS 的全名是 HTTP Strict Transport Security,聽過這個 header 的人可能比較少一點,他是用來強制瀏覽器只能使用安全的 HTTPS 協定跟網站進行連線,而不能使用 HTTP

譬如說很多網站其實用 HTTP 跟 HTTPS 都連得上,但考量到安全性,當然是希望使用者都走 HTTPS。這時只要在 header 裡加上 Strict-Transport-Security: max-age=31536000; includeSubDomains,那在往後的 31536000 秒內(其實就是一年啦XD),只要使用者的瀏覽器看到這個網域或他的子網域,就會全部改成用 HTTPS 進行連線,真的是很方便呢

也因為現在網站幾乎都有 HTTPS 了,所以像平常會用到的 Google、Medium、Facebook 都可以看到 HSTS 的蹤跡,只是大家設定的 max-age 稍有不同,比較常看到的大概會是 31536000(一年)、15552000(180 天)這類的數字

雖然在 HTTP header 裡面加上 HSTS 很簡單,但如果你還是覺得麻煩,而且你又剛好有在用 Cloudflare 代管 DNS,那也可以參考這篇文章直接在 Cloudflare 設定裡面的 Cryptography 啟用 HSTS,那就什麼都不用做哦~

X-Content-Type-Options

聽過這個 header 的人可能又更少了,在講這個 header 之前,得先來談談什麼是 content sniffing:一般來說瀏覽器會透過 Content-Type 來判斷請求到的資源是什麼類型,像透過 <script src="script.js"> 拿到的 Content-Type 一般都是 text/javascript,因此瀏覽器看到之後就會拿來執行

但有些網站(尤其是十幾二十年前的舊網站)在開發時並沒有把 Content-Type 設好,導致某些 JS 檔的 Content-Type 是 text/plain,也就是純文字檔。為了讓這些網站可以順利運作,瀏覽器除了參考 Content-Type 之外,也會做 content sniffing 從檔案內容分析是什麼類型,如果分析出是 JS 那就會拿去執行,這樣舊網站才不會壞掉

但 sniffing 這個動作看似貼心,卻也是一個弱點。譬如說有些網站允許使用者上傳檔案,那攻擊者就可以惡意上傳一些有 JS 特徵的 JPG 檔(這些圖片會被瀏覽器判斷成腳本)。接著想辦法讓這張圖片被載入到前端來,導致裡面的惡意腳本被執行,造成 XSS 攻擊

為了防止瀏覽器在那邊亂猜檔案的 Content-Type 是什麼(而且麻煩的是每個瀏覽器猜的方式還不太一樣),我們要在 headers 裡面加上 X-Content-Type-Options: nosniff 告訴瀏覽器直接用 header 裡面提供的 Content-Type 就好,不要在那邊瞎猜,如此一來就不會再有純文字、圖片被判斷成腳本這種事

但也因為加了 nosniff,所以務必要注意各種資源的 Content-Type 有沒有設定好,因為瀏覽器不會幫你猜,如果你真的把 JS、CSS 的 Content-Type 設錯,那瀏覽器就不會把它跑起來,網站看起來也就怪怪的

X-Frame-Options

平常在寫網頁時,若是想把其他網頁的內容拿過來用(下圖),可以用 <iframe src="https://website.com"> 把他嵌入進來;反之,若是別人想把我做的網站嵌入到他的網站裡面也是可以的

那這樣會有什麼資安疑慮呢?萬一有個壞壞網站,他透過 iframe 把氣象局網頁嵌入進去後,用 CSS 把那個 iframe 調成透明的,然後在透明的 iframe 背後放一些按鈕(下圖)。那使用者在壞壞網站上點擊我很帥、我帥爆時,就會不小心點到氣象局的網站,這種攻擊就稱作 Clickjacking

如果點到的只是氣象局網站那不會怎麼樣,反正怎麼點也就是那樣。但萬一被嵌入的是某銀行的網站呢?那使用者可能就會被精心設計的按鈕給騙到,在無意識的情況下就按了 iframe 裡面的轉帳、提款等等按鈕

為了要避免這類問題,最好的方法就是加上 X-Frame-Options: deny 這個 header,意思是告訴瀏覽器說我這個網站不想被嵌入(為了安全起來大部分的銀行都會加這個 header)

所以當我想在自己的網站上把玉山銀行的頁面嵌入進來時,瀏覽器就會噴出「ebank.esunbank.com.tw 不允許在被別的網站嵌入」的錯誤。因為玉山銀行的頁面根本不允許被嵌入,也就避免了基於 iframe 的 Clickjacking 攻擊

總結

今天介紹了 CSP、HSTS、X-Content-Type-Options 跟 X-Frame-Options 總共四個跟安全性有關的 HTTP headers,這些 headers 不只用起來輕鬆簡單,加上去根本不用十分鐘,而且又可以大幅提高安全性,所以已經是網站開發不可缺少的 HTTP headers 了

除了這四個之外其實還有 X-XSS-Protection、Expect-CT、Public-Key-Pins、Feature-Policy 等等很多 header,但因為有些效果沒那麼大、有些則是已經被宣佈棄用了,所以這邊就暫且不提,有興趣可以自己到 Owasp Secure Headers 上面看看~

延伸閱讀

--

--

Larry Lu
Starbugs Weekly 星巴哥技術專欄

我是 Larry 盧承億,傳說中的 0.1 倍工程師。我熱愛技術、喜歡與人分享,專長是 JS 跟 Go,平常會寫寫技術文章還有參加各種技術活動,歡迎大家來找我聊聊~