前篇“WEB安全防護相關響應頭(上)”中,我們分享了 X-Frame-Options、X-Content-Type-Options、HTTP Strict Transport Security (HSTS) 等安全響應頭的內容。下文中,我們則側重介紹一些和跨站安全相關的響應頭——
一、Referrer-Policy -- 不要問我從哪里來
“互聯網”這個詞,顧名思義,“互聯”才有意義。我們看到的一個常規頁面,往往是先加載父級頁面,父級頁面再加載其他的子資源(如圖片、JS 文件和各種多媒體文件等);主頁面上通常還有各種鏈接,點擊會跳轉到其他內容;另外,通過 < iframe > 等標簽,還可以把第三方頁面嵌入在父級頁面里直接顯示出來。這些都是內容之間“互聯”的體現。

▲圖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
任何情況下,瀏覽器都不發送 HTTPreferer請求頭; -
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 正常狀態下圖片訪問帶referer請求頭
如果 www.sandbox.com 服務器的 Nginx 配置內,加入 add_headerReferrer-Policy"same-origin"always; ,設定只有同源站點才發送 Referer 請求頭。這時候,訪問 http://www.sandbox.com/index.html 獲得的響應頭里,就增加了一行 Referrer-Policy:same-origin 的響應頭,如下:

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

▲圖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 JavaScript代碼執行成功,看到彈窗
測試二
設置 X-XSS-Protection:1。
這是默認設置。也就是說,如果服務器端完全沒有返回過 X-XSS-Protection 響應頭,瀏覽器就認為服務器端返回的是 X-XSS-Protection:1 。這時候,瀏覽器會根據自己的內部過濾原則,直接無視它認為有問題的那部分代碼,自動跳過這部分代碼(這部分內容根本不會發給服務器端),最終我們看到的是“清理”后的效果:

▲圖6 JavaScript代碼沒有執行成功,沒有彈窗
測試三
設置 X-XSS-Protection:1;mode=block。
這是最嚴厲的設置。這時候,瀏覽器會根據自己的內部過濾原則,發現有問題代碼,直接就拒絕顯示該頁面,這次提交也不會被發往服務器端,效果如圖:

▲圖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
- ‘Referrer Policy’ - Editor’s Draft, 20 April 2017
- ‘Referrer-Policy’ - developer.mozilla
- ‘X-XSS-Protection’ - developer.mozilla
- The Misunderstood X-XSS-Protection
