Web 應用程式開發者,無論是前端或後端,多少都要認識 HTTP,在 HTTP 的相關知識中,最為人知曉卻也最被人忽略的是標頭(Header),伺服端的回應標頭除了控制瀏覽器對內容類型、快取、編碼等的處理之外,現在也可用來控制現代瀏覽器上安全措施應有之行為。
- HTTP 安全標頭
談到 HTTP 標頭的同時又要涉及安全議題時,Web 應用程式開發者可以聯想到的標頭有幾個呢?至少多少都要聯想到 Set-Cookie
,其中可以附加 HttpOnly
以及 secure
,前者控制 Cookie只能用在 HTTP 傳輸上,從而限制了 JavaScript 無法讀取 Cookie,這可以用來避免會話劫持(Session hijacking),後者要求 Cookie 只在加密連線請求時才能發送給瀏覽器,實際上這在現代瀏覽器中更為嚴格,例如 Chrome 52 後,非加密連線請求下,瀏覽器不會接受 secure
的 Cookie 設置。
對於曾經處理跨域請求的開發者來說,應該也接觸過 CORS 規範,伺服端可以藉由 Access-Control-Allow-Origin
等標頭,控制瀏覽器是否允許指令稿拿到相關回應,另外也可使用 Vary
來控制瀏覽器對相同 URL 資源的快取行為,這在先前專欄〈深入認識跨域請求〉曾經探討過。
然而,試著用瀏覽器開啟「Security Headers」這個網站,輸入你的網站網域名稱後按下「Scan」,你會不會得到一個大紅的「F」呢?Security Headers 會檢測指定的網址,是否傳回預期的七個與安全有關的回應標頭,以及其設定內容,從而決定出網站的安全指數。
實際上與安全相關的回應標頭不只七個,OWAS P的 Secure Headers Project,列出了安全相關的標頭,各瀏覽器在哪個版本實現、各大網站採用的現況、相關的增刪變更以及可用的檢測工具等資訊,在它的「Best Practices」中,也列出了這些安全標頭的相關說明。
- 控制瀏覽器的安全措施
現代瀏覽器如 Chrome、Firefox、Safari 甚至是 IE,都實作了一組可被 HTTP 回應標頭控制的安全措施,在沒有進一步被相關標頭指示下,這些安全措施會有預設行為,然而 Web 開發人員或網站管理者,可以藉由標頭來改變其行為。
以 XSS 的防護為例,有些瀏覽器在檢測到發出的請求與得到的回應中,若包含了相同的(惡意)指令稿,預設就會封鎖指令稿執行並不顯示頁面內容(Chrome 78 以後取消了此功能),X-XSS-Protection
標頭可用來控制是否關閉該功能(0
為關閉)、過濾指令稿後是否顯示內容(1; mode=block
是封鎖)、或者回報至指定網址(1;report=your-url
),當然,這主要是針對反射式 XSS 的預防。
X-Frame-Options
可用來控制瀏覽器,可否在 iframe
中顯示網站內容(DENY
、SAMEORIGIN
或者用 ALLOW-FROM
設置白名單),這不是為了避免自家內容被另的網站蓋台顯示,主要是用來避免介面偽照(UI Redressing)的相關攻擊,像是在近乎透明的 iframe
中載入惡意網站,實現點擊劫持(ClickJacking)或提取使用者輸入內容等攻擊。
若伺服器回應時沒有指定 Content-Type,瀏覽器會試著探嗅探(Sniff)回應類型,並進行探測到的類型應有之行為,如果惡意地偽裝為圖片連結,然而內容實際上是指令稿,在沒有指定 Content-Type
的情況下,瀏覽器就有可能執行指令稿,為了避免這類問題,X-Content-Type-Options
可以設置為 nosniff
,不過要注意,雖然字面上是用來關閉瀏覽器的嗅探行為,然而因為相容性的問題,實際上只會套用在 script
與 style
,要求這兩者必須在有正確的內容類型指定下才能運作。
Referer
標頭(在規範中被拼錯)用來表示請求是從哪個頁面連結而來,有助於對於網站上做些來源統計,然而有時會意外地洩漏敏感訊息,像是附在網站上的會話 ID 等,可以運用 Referrer-Policy
的八個值設定,規範在是否同源、加密連線等的情況下,瀏覽器才附上 Referer
標頭,當然也設定 no-referrer
來完全關閉 Referer
標頭之使用。
Strict-Transport-Security
可用來強制要求瀏覽器,就算一開始是 http://
,接下來的期間設定內(max-age
,以秒為單位),瀏覽器都要使用加密連線,設定時可決定是否包含子域(includeSubDomains
);Feature-Policy
是個相對比較新的標頭,控制的是瀏覽器的特性,可用來決定瀏覽器是否可使用某些 API 或設備(例如相機頭),Chrome 60 後才加入此支援。
- 內容安全策略
Content-Security-Policy
是設定上最多樣、最繁複,然而值得特別討論的安全標頭,先前談過,X-XSS-Protection
主要是針對反射式 XSS 的預防,對於其他類型的 XSS,往往有賴於 Web 應用程式在實作上加以防堵,Content Security Policy
(CSP)是 2010 年被提出來討論的規範,其目的在於從根本上,禁止瀏覽器從非預期的來源載入惡意指令稿。
CSP 規範在提出討論之後,在發展的過程中,各家瀏覽器就在試著實現了,因此這中間除了 Content-Security-Policy
之外,還出現了 X-Content-Security-Policy
、X-WebKit-CSP
等非標準標頭,CSP 在 2015 年 1.0 正式釋出,2016 年來到 2.0,目前 3.0 正在草案階段。
XSS 的問題,根本上是瀏覽器不知道哪些來源的資源是可信任的,CSP 簡單來說就是設置白名單,明確告知瀏覽器,只能夠從哪個來源載入資源,可控制的資源包括了 script
、style
、img
、media
、object
、frame
、font
等,default-src
可以設定預設值,例如若沒有設定 script-src
的名單,那就看 default-src
中的名單,確定可否載入指令稿,對於不在白名單的載入行為,report-uri
可用來要求瀏覽器發送報告至指定網址。
然而使用白名單設置是個麻煩的過程,由於現代 Web 應用程式,往往會從多個來源(CDN之類)載入資源,過於嚴格的白名單,甚至會造成網站來源間的設置衝突,為此,CSP 也有一些寬鬆的設定選項(像是 unsafe-inline
等),不過往往也被濫用,若沒有正確地設置這類選項,就會被繞過規則而無法有效防範 XSS,在網站上搜尋「CSP bypass」就可以找到大量的案例。
為了協助運用 CSP 防堵 XSS,也為了能正確地、安全地設置 CSP 選項,Google推出了 CSP Evaluator,可以把編寫好的CSP貼上去,以檢測內容是否正確以及是否有安全問題,也可以看看其中的「Sample unsafe policy」,這看似設定得很用心的 CSP 範例,其實問題多多。
- 被動防守與主動防堵
各類型 HTTP 伺服器,都會有方式可以全域地設置這些安全性標頭,有些 Web 框架也會提供相關設定,各個安全標頭的作用與設定細節,只要在網路上搜尋「HTTP Header Security」,都可以找到相關的說明文件,因此重點應擺在認識這些文件背後著墨的安全議題。
Web 應用程式過去在安全性這方面,往往只能被動地等待瀏覽器的請求,看看是否有需要防堵的攻擊,甚至連使用者被攻擊了都不知道(像是 DOM-based XSS);HTTP 安全相關的標頭,可以主動地要求瀏覽器,積極地遵守標頭的指示,不發起非安全的操作,若能善加利用,對 Web 應用程式的安全性,也會是不小的助益。