HSTS是讓瀏覽器強制使用HTTPS訪問網站的一項安全策略。HSTS的設計初衷是緩解中間人攻擊帶來的風險。本文主要介紹HSTS及其他Web功能帶來的一些隱私問題,比如如何利用它們來探測瀏覽器的用戶歷史紀錄。
一、背景:什么是HSTS
HSTS的英文全稱是HTTP Strict Transport Security,中文譯作HTTP嚴格傳輸安全。2012年11月IETF發布RFC 6797,在這篇文檔中正式定義了HSTS。HSTS的開啟方式是在HTTP響應頭中加入Strict-Transport-Security字段。如:Strict-Transport-Security: max-age=31536000 。這意味著在接下來的31536000秒內(1年),當瀏覽器需要訪問同一個域名時,必須使用HTTPS,并且用戶不可以忽略證書錯誤警告。使用HSTS避免了一系列的中間人攻擊問題,比如HTTPS剝離攻擊 [1]、HTTPS Cookie注入攻擊 [2]等。
設置HTTP響應頭的方法雖然可以規避大量的中間人攻擊,但是用戶的第一次訪問仍然是不受HSTS保護的。于是誕生了瀏覽器預置HSTS列表。網站站長可以主動向Chrome團隊提交自己的域名。批準后,各主流瀏覽器廠商(不只是Chrome)會在編譯新版瀏覽器時將你的域名硬編碼進內置HSTS列表中。
現在已經有越來越多的網站開啟了HSTS,比如Google、百度、支付寶等。根據trustworthyinternet.org 發布的SSL Pulse報告顯示,截至2017年5月,有11.8%的網站支持HSTS [3]。最新版的主流瀏覽器也都支持HSTS,比如Chrome、Edge、IE 11、Firefox、Opera、Safari等。
二、漏洞一:利用端口號和標簽探測歷史紀錄
上一節所述的都是HSTS好的一方面,下面來說HSTS導致的問題。第一個漏洞是我和Vlad Tsyrklevich在2014年獨立發現的 [4][5]。簡單來說,如果www.example.com開啟了HSTS,如果用戶沒有訪問過它,那么http://www.example.com:443/favicon.ico一定會訪問失敗。如果訪問過,那么HSTS會使瀏覽器請求https://www.example.com:443/favicon.ico,這樣就會成功(如果不存在favicon.ico這個圖片的話,就任選一個這個域名下其他圖片地址)。所以我們用http://www.example.com:443/favicon.ico" onerror="not_visited()" >,如果onerror被調用就說明沒有訪問過www.example.com,如果onload被調用就說明訪問過。
這個方法有一定的限制,比如被測試的域名必須要使用HSTS,并且不能在HSTS預置列表中。而且只能判斷一個域名是否訪問過,而無法測試整個URL是否被訪問過。
這個漏洞我報給了Chromium團隊,報告和完整PoC可參見 [4]。我的建議是禁止http協議使用443端口。但是由于這樣會給WebSocket造成兼容性問題,并且這個漏洞影響小,所以他們最終決定不修復這個漏洞。
網站可以把自己的域名提交到HSTS預置列表來規避這個漏洞。用戶可以通過清空歷史紀錄避免這個漏洞,因為清空歷史記錄會同時清空動態設定的HSTS記錄。
三、漏洞二:Sniffly — 利用HSTS和CSP探測歷史紀錄
這個漏洞是由雅虎的安全工程師Yan Zhu于2015年發現的。她在Toorcon 2015會議上講述了這個漏洞(演講視頻參見[6],幻燈片參見[7]),并把這個漏洞命名為Sniffly。Freebuf之前也有一篇文章《Sniffly: 利用HSTS和CSP嗅探瀏覽器歷史記錄》[8],就是寫這個漏洞的。
這個漏洞利用CSP(內容安全策略)來阻止https協議的圖片,而同時允許http協議。這個CSP是這樣設置的:Content-Security-Policy: img-src http://*。這樣如果有一個http到https的重定向,那么這個CSP將在這個重定向發生之后,阻止https請求,并調用onerror handler。攻擊者可以使用JavaScript來測從http請求發出到https被阻止之間的時間間隔,這個時間間隔就是重定向所需時間。如果這個時間很短(小于10毫秒),那么我們可以認為瀏覽器沒有向服務器發送任何請求,也就是說這個重定向來源于HSTS或者是緩存的301重定向。這樣我們就知道用戶曾經訪問過這個域名。
這個漏洞很快地在Chrome中修復了,漏洞編號是CVE-2016-1617。修復方法是:如果CSP中指定了http://*,則它同時允許http和https協議。這樣就沒法用這個方法屏蔽http到https的重定向。Yan Zhu給Chrome提交的漏洞報告和PoC可參見 [9]。
四、漏洞三:利用HSTS、CSP和端口號探測歷史記錄
這個漏洞是我在2016年,看完漏洞二的細節后想出來的繞過方法。首先我們看Google對漏洞二的修復代碼 [10]:
補丁在WebKit/Source/core/frame/csp/CSPSource.cpp文件中的CSPSource::schemeMatches函數中加入了下面4行代碼:
if (equalIgnoringCase(m_scheme, "http"))
return equalIgnoringCase(url.protocol(), "http") || equalIgnoringCase(url.protocol(), "https");
if (equalIgnoringCase(m_scheme, "ws"))
return equalIgnoringCase(url.protocol(), "ws") || equalIgnoringCase(url.protocol(), "wss");
這個代碼的意思就是當CSP中的協議是http時,url的協議是http或https都能成功匹配。ws是WebSocket協議,同樣CSP中指定的ws協議可以同時匹配ws和wss。
很顯然這個修復只考慮了URL中的協議部分,所以我想到利用漏洞一中的技巧,我們在CSP中顯式指定端口號,就繞過了修復。
比如,我們設置這個CSP策略:img-src http://example.com:80。漏洞二修復之后,這個CSP會允許http://example.com:80和https://example.com:80,但是后一個URL并沒有意義,因為https不用80端口,而真正的https://example.com依然被阻止,因為https的端口號不匹配”:80”。有了這個思路之后,剩下的利用方法就和漏洞二一樣了,也是測http到https的重定向時間。
這個漏洞同時存在于Chrome、Firefox、WebKit。但Edge、IE不存在這個漏洞。Edge是在https請求返回之后才調用onerror,所以Edge中無法計算重定向時間。
給Chrome的報告和PoC在[11],給Mozilla的報告在[12],給WebKit的報告在[13]。他們都早已修復完畢。漏洞編號是CVE-2016-5137(Chrome)和CVE-2016-9017(Firefox)。Google還給了我1000美元獎金。
五、總結
這篇文章主要介紹了什么是HSTS以及和HSTS相關的三個漏洞。這三個漏洞影響都不大,但是我寫出來主要為了分享,如何靈活運用端口號這個技巧來繞過相關限制。HSTS其實還能當Cookie用,也是HSTS帶來的隱私問題,鑒于和本文關系不大,就不涉及了
|