WEB安全防護相關響應頭(下)

天存資訊發表於2021-06-04

前篇“WEB安全防護相關響應頭(上)”中,我們分享了 X-Frame-Options、X-Content-Type-Options、HTTP Strict Transport Security (HSTS) 等安全響應頭的內容。下文中,我們則側重介紹一些和跨站安全相關的響應頭——

一、Referrer-Policy -- 不要問我從哪裡來

“網際網路”這個詞,顧名思義,“互聯”才有意義。我們看到的一個常規頁面,往往是先載入父級頁面,父級頁面再載入其他的子資源(如圖片、JS 檔案和各種多媒體檔案等);主頁面上通常還有各種連結,點選會跳轉到其他內容;另外,通過 < iframe > 等標籤,還可以把第三方頁面嵌入在父級頁面裡直接顯示出來。這些都是內容之間“互聯”的體現。

圖1
▲圖1 一些做得好的 <iframe> 會和主頁面融為一體,如右側

在 HTTP 協議裡,如果【A資源】發起了對【B資源】的互聯請求,表明該請求來自【A資源】的資訊會體現在【B資源】的「referer」請求頭裡。【B資源】就能明確知道發起方是【A資源】。只要是引用關係或者互聯關係的請求,瀏覽器都會自動附加這個「referer」請求頭,以標明發起端是誰。

這個請求頭的本意,是讓網站管理者更容易得知 HTTP 訪問的來源。但人們逐漸認識到這個請求頭有可能暴露使用者的隱私。譬如【A資源】當前的 URL 裡,如果包含了比較敏感的使用者名稱、許可權和會話等資訊;只要捕獲【B資源】的「referer」請求頭,就有可能獲得使用者在【A資源】裡的敏感資訊。


出於對保護隱私的考慮,Firefox 和 Chrome 等瀏覽器引入了一套更精確控制瀏覽器如何傳送「referer」請求頭的機制,名叫「Referrer-Policy」。支援這套機制的瀏覽器,會根據具體情況決定是否傳送「referer」請求頭。但值得注意的是:微軟系列的瀏覽器IE和Edge都不支援這個機制

使用以下幾種方式,可以載入和設定不同的「Referrer-Policy」策略:

方法一:

從 WEB 伺服器端,整體地返回 Referrer-Policy 響應頭:

#Nginx配置:
add_header Referrer-Policy "no-referrer" always;

#Apache配置:
Header always set Referrer-Policy  "same-origin"

方法二:

對整個頁面新增一個名為"referrer"的新 meta 值,類似:

<meta name="referrer" content="origin">

方法三:

給頁面內某個標籤,如下例中的 <a> 連結標籤和 <img> 圖片載入標籤,增加一個 referrerpolicy 屬性:

<a href="http://example.com" referrerpolicy="origin">
<img src="http://www.baidu.com/img/bd_logo1.png" referrerpolicy="no-referrer">

可以看出,以上三種方式的生效範圍各有差異,分別對應整站起效、特定頁面起效及設定特定標籤起效,可以根據具體情況使用。

這個策略可以配置為以下值,含義分別為:

  • no-referrer
    任何情況下,瀏覽器都不傳送 HTTP referer 請求頭;

  • no-referrer-when-downgrade
    如果瀏覽器從 HTTPS 型別的 URL 跳轉到 HTTP 型別的 URL,瀏覽器就不需要傳送 referer 請求頭;

  • same-origin
    只有發起端和目標端是同源時,瀏覽器才傳送 referer 請求頭。域名和協議完全相同,兩個站點才是同源站點;

  • origin
    瀏覽器會傳送 referer 請求頭,但 referer 請求頭裡只有發起方的域名資訊,沒有完整的 URL 路徑。如發起端 URL 為 https://example.com/page.html,實際傳送的 referer 請求頭裡只有 https://example.com/

  • strict-origin
    origin 含義相似,且只有同等安全級別的協議才傳送 referer 請求頭,如從 HTTPS→HTTPS 會傳送,而從 HTTPS→HTTP 則不傳送;

  • origin-when-cross-origin
    對同源的其他資源,傳送包含完整 URL 的 referer 請求頭;如果是非同源的資源,則 referer 請求頭裡只有域名資訊,沒有完整 URL;

  • strict-origin-when-cross-origin
    和上一條類似,但協議的安全等級降低時就不傳送 referer 請求頭了;

  • unsafe-url
    無論是否同源,都傳送完整 URL 的 referer 請求頭。

舉例:在我們的測試頁面 http://www.sandbox.com/index.html 裡,包含外站圖片 http://img.tcxa.com.cn/logo.png 。預設在沒有其他設定的情況下,發往該圖片的請求如下圖,其中的 Referer 請求頭裡清晰地包含了父級頁面的地址:http://www.sandbox.com/index.html

圖2
▲圖2 正常狀態下圖片訪問帶referer請求頭

如果 www.sandbox.com 伺服器的 Nginx 配置內,加入 add_headerReferrer-Policy"same-origin"always; ,設定只有同源站點才傳送 Referer 請求頭。這時候,訪問 http://www.sandbox.com/index.html 獲得的響應頭裡,就增加了一行 Referrer-Policy:same-origin 的響應頭,如下:

圖3
▲圖3 父資源的Referrer-Policy設定為same-origin同源

這時候,由於和發起端 www.sandbox.com 的域名並不同源,如下圖所示,可以看出,發往 http://img.tcxa.com.cn/logo.png 圖片的請求此時已不再出現 Referer 請求頭了:

圖4
▲圖4 子資源的請求裡不再包含Referer請求頭

題外話:
referer 這個單詞在英語裡並不存在,它是個拼錯的單詞,正確寫法是「referrer」。在相關 HTTP 協議制定時,寫作者筆誤寫錯了。人們意識到這個錯誤時為時已晚。為了保持舊有相容性,這個名字被將錯就錯延續下來。但後續很多和 referer 請求頭相關的術語和協議,又恢復了正確的「referrer」拼法。比如這裡的「Referrer-Policy」策略。

二、X-XSS-Protection -- 跨站邊界保護

XSS 的全稱是 Cross Site Scripting,中文叫“跨站指令碼攻擊”。其中“指令碼”一詞,主要指 JavaScript 指令碼。JavaScript 指令碼在多年的進化中,使用越來越靈活,功能越來越強大,這也導致人們原本不太在意的瀏覽器客戶端安全,變得越來越重要了。

現在的 JavaScript 指令碼,不但可以訪問和操控頁面上的 DOM 元素,還可以和伺服器端進行互動,故而它帶來的安全隱患也不容忽視。為了“緩解”這一問題,瀏覽器廠商們做了一定的努力,其中一種機制就是 X-XSS-Protection 響應頭。支援這一響應頭的瀏覽器,在檢測到跨站指令碼攻擊 (XSS)時,可以主動停止載入頁面。

這個響應頭有以下四種值:

X-XSS-Protection: 0
X-XSS-Protection: 1
X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; report=<reporting-uri>

這四個值的含義分別為:

  • 0 :禁用對頁面的 XSS 過濾功能;
  • 1 :啟用對頁面的 XSS 過濾功能,這也是瀏覽器預設的處理(不需要做任何配置,就是這個選項)。如果發現有 XSS 風險的程式碼,瀏覽器就自動清理頁面,去除這部分有危害的程式碼;
  • 1;mode=block :啟用對頁面的 XSS 過濾功能,但在發現 XSS 風險時,會直接遮蔽整個頁面的展示,而不是隻去除有風險部分;
  • 1;report=<reporting-URI> :啟用對頁面的 XSS 過濾功能,如果發現有 XSS 風險的程式碼,瀏覽器就自動清理頁面,去除這部分有危害的程式碼,同時,把有問題的事件緣由提交給指定的 URL。

我們用 DVWA 的跨站演示頁面,來分別展示一下,響應頭設定為上述幾個不同值時,對應的不同效果。

以下三次測試,都是提交了完全一樣的請求:

http://dvwa站點IP/vulnerabilities/xss_r/?name=<script>alert(document.cookie)</script>

測試一

設定 X-XSS-Protection:0

X-XSS-Protection:0 時,瀏覽器直接執行了有問題的網頁端程式碼,所以,提交的內容裡的 JavaScript 程式碼能成功執行,在瀏覽器裡看到了彈窗效果,彈窗內容為瀏覽器訪問當前網站的 Cookie 值,參見圖5。這種設定僅適用於安全滲透測試練手,以及希望準確評估網站安全風險程式碼時使用。

圖5
▲圖5 JavaScript程式碼執行成功,看到彈窗

測試二

設定 X-XSS-Protection:1

這是預設設定。也就是說,如果伺服器端完全沒有返回過 X-XSS-Protection 響應頭,瀏覽器就認為伺服器端返回的是 X-XSS-Protection:1 。這時候,瀏覽器會根據自己的內部過濾原則,直接無視它認為有問題的那部分程式碼,自動跳過這部分程式碼(這部分內容根本不會發給伺服器端),最終我們看到的是“清理”後的效果:

圖6
▲圖6 JavaScript程式碼沒有執行成功,沒有彈窗

測試三

設定 X-XSS-Protection:1;mode=block

這是最嚴厲的設定。這時候,瀏覽器會根據自己的內部過濾原則,發現有問題程式碼,直接就拒絕顯示該頁面,這次提交也不會被髮往伺服器端,效果如圖:

圖7
▲圖7 整個頁面都無法顯示了

以上三種設定,可以根據具體的需求做選擇。

如果需要在伺服器端設定這個響應頭,可以在合適的範圍內,加入以下指令:

#Nginx配置:
add_header X-XSS-Protection "1; mode=block" always;

#Apache配置:
Header always set X-XSS-Protection "1; mode=block"

那麼,是不是我們只要給伺服器設定好這個響應頭,就能徹底解決跨站指令碼攻擊的問題呢?答案有點令人喪氣:並不一定!這個機制的定位僅僅是“緩解”跨站指令碼攻擊,它不是一顆銀子彈,無法就此高枕無憂了。一方面,跨站指令碼攻擊有非常多的變型手法和實現,業界公認沒法完全通過黑名單規則來徹底過濾跨站——要徹底防護跨站指令碼攻擊,就幾乎需要牴觸網際網路的“互聯”本質。所以,X-XSS-Protection 的機制,也只是對跨站指令碼攻擊的部分防護。

另一方面,也請閱讀附錄“參考”裡的第4條連結裡的內容。這位作者對 X-XSS-Protection:1 的設定尤為意見大,因為攻擊者反而有可能巧妙地利用這個機制,使網站需要正常使用的 JavaScript 指令碼,被 X-XSS-Protection 機制判斷為有危害,導致整個 JavaScript 指令碼無效,又引入其他的安全問題。所以他的建議是,如果很確定自己的網站沒有跨站問題或無法忍受自己的頁面被誤判有跨站,就設定 X-XSS-Protection:0;否則就明確禁用有問題的整個網頁,使用 X-XSS-Protection:1;mode=block 設定項。

要對客戶端進行更細粒度更有效的安全防護,目前更建議使用的機制是 CSP (Content Security Policy)。這個又需要一篇獨立的文件來介紹了,敬請期待。

(朱筱丹 | 天存資訊)


Ref

  1. Referrer Policy’ - Editor’s Draft, 20 April 2017
  2. Referrer-Policy’ - developer.mozilla
  3. X-XSS-Protection’ - developer.mozilla
  4. The Misunderstood X-XSS-Protection

相關文章