同源策略
- 同源策略是web頁面安全中心最基礎、最核心的安全策略。
- 如果兩個URL的協議、域名和端口都相同,我們就稱這兩個URL同源。
同源策略的表現
1. DOM層面
同源策略限制了不同源的JavaScript腳本對當前DOM對象讀寫的操作。
// a網頁控制檯輸入
window.open("b.test.com")
// b網頁控制檯輸入
opener.document.body.style.display = "none"
如果兩個網頁同源,第二個b網頁控制檯可以修改a網頁控制檯DOM。如果兩個網頁不同源,b網頁無法獲取到a網頁的document對象。
2. 數據層面
同源策略限制了不同源的站點讀取當前站點的Cookie、IndexDB、LocalStorage等數據。
// a網頁控制檯輸入
window.open("b.test.com")
// b網頁控制檯輸入
opener.localStorage
3. 網絡層面
跨域問題
安全與便利的權衡
不同源之間絕對隔離無疑是最安全的,但是也使得Web項目難以開發和使用。因此瀏覽器實現上出讓了同源策源的部分安全性。
1. 頁面中可以引入第三方資源
同源策略需要一個頁面的所有資源都來自於同一個源,無疑違背了Web開放的初衷。所以最初的瀏覽器都是支持外部引用資源文件的。但是這也造成了Web頁面被XSS攻擊的風險。
2. 跨域資源共享與跨文檔消息機制
- 爲了解決跨域問題,引入了跨域資源共享(CORS),使用該機制可以進行跨域訪問控制,從而保證跨域數據傳輸的安全進行,但是這也使得頁面存在被CSRF攻擊的風險。
- 爲了解決不同源的DOM之間進行通信,瀏覽器引入了跨文檔消息機制,可以通過window.postMessage來和不同源的DOM進行通信(使用過程中message事件必須要使用origin和source屬性來檢查消息的發送者的身份,否則存在XSS攻擊的風險)
// a網頁控制檯輸入
window.open("b.test.com")
window.addEventListener("message",event=>{
console.log(event)
})
// b網頁控制檯輸入
opener.postMessage("123","http://a.test.com")
跨站腳本攻擊(XSS)
XSS全稱Cross Site Scripting,爲了與css樣式區分開,所以成爲XSS。XSS攻擊是指黑客往HTML文件或者DOM中注入惡意腳本,從而在用戶瀏覽網頁時對用戶實施攻擊的一種手段。最開始的時候,這種攻擊是通過跨站點實現的,但是發展到後面,注入惡意腳本的方法越來越多,但是XSS的這個名字一直保留下來。
XSS的危害
惡意腳本可以做很多事情,下面舉幾個例子。
- 竊取Cookie信息,惡意腳本獲取到用戶cookie信息,通過fetch或者XMLHttpRequest加上CORS功能將數據發動給惡意服務器,惡意服務器拿到cookie以後就可以在其他電腦上模擬用戶的登錄。
- 監聽用戶行爲,惡意腳本通過“addEventListener”監聽鍵盤事件,獲取用戶輸入的銀行卡、密碼等信息,並將其發送到惡意服務器。
- 通過修改DOM僞造假的登錄窗口,欺騙用戶輸入用戶名與密碼。
- 在頁面內生成浮窗廣告。
常見的XSS攻擊方式
1. 存儲型XSS攻擊
存儲型XSS攻擊的幾個步驟:
- 黑客利用網站漏洞將惡意腳本提交到網站的數據庫中;
- 用戶向網站請求包含了惡意腳本的網頁,或者ajax請求返回的惡意腳本數據被插入到DOM中。
上面的例子中,如果前端網頁直接將後端返回的數據顯示在網頁上,所有訪問此文章的用戶都將會收到攻擊。
2. 反射型XSS攻擊
服務端將未經處理的url參數直接渲染到頁面上。存在此漏洞的網站黑客可以通過聊天軟件或者郵箱誘導用戶點擊惡意鏈接。
// 正常請求
www.test.com?id=123
// 惡意鏈接
www.test.com?id=<script>alert(123)</script>
3. 基於DOM的XSS攻擊
不涉及Web服務器的情況下也可以進行XSS攻擊。比如通過網絡劫持修改HTML頁面的內容,例如通過路由器或者本地惡意軟件來劫持。
XSS真實案例
如何阻止XSS攻擊
存儲型XSS與反射型XSS攻擊屬於是服務端的安全漏洞,基於DOM的XSS攻擊屬於前端漏洞。但是無論哪種形式的攻擊共同點都是首先向頁面中插入惡意腳本。
1. 服務器對輸入進行轉碼
// 轉碼前
<script>alert('XSS攻擊')</script>
// 轉碼後
<script>alert('XSS攻擊')</script>
轉碼後的內容即使插入到頁面中也不會執行。
有些站點會對<script>關鍵字進行過濾,但是這樣並不完全安全,例如利用less可以執行JavaScript的漏洞進行攻擊。
<!DOCTYPE html>
<html lang="en">
<style type=text/less>a{a:`alert(123)`}</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/less.js/2.7.1/less.min.js" ></script>
</html>
2. 使用CSP策略
csp策略可以通過配置"content-security-policy"響應頭有效的防範XSS攻擊。CSP策略有如下幾個功能:
- 限制加載其他域的資源;
- 禁止像第三方域提交數據;
- 禁止執行內聯腳本和未授權的腳本;
- 上報機制,幫助站點儘快發現有哪些XSS攻擊。
雖然CSP可以有效的防禦XSS攻擊,但是由於限制較多所以實際使用的web網頁並不多。可以參考github、知乎。
3. 使用HttpOnly屬性
由於很多XSS攻擊都是用來盜用Cookie的,因此還可以通過HttpOnly屬性標記重要的cookie,是得其只能在HTTP請求過程中訪問,無法通過javaScript腳本讀取。
跨站請求僞造(CSRF)
CSRF全稱是Cross-site request forgery。CSRF攻擊是指黑客利用了用戶的登錄狀態,並通過第三方的站點做一些壞事。
常見的CSRF攻擊方式
1. 自動發起Get請求
<!DOCTYPE html>
<html>
<body>
<h1>惡意站點</h1>
<img src="https://www.test.com?user=hacker&number=100">
</body>
</html>
黑客將請求接口隱藏在img標籤內,當頁面被加載時就會自動發起img的資源請求,如果服務器沒有做過濾判斷的話,服務器就會認爲該請求是一個轉賬請求。
2. 自動發起POST請求
<!DOCTYPE html>
<html>
<body>
<h1>惡意網站</h1>
<form id='hacker-form' action="https://www.test.com" method=POST>
<input type="hidden" name="user" value="hacker" />
<input type="hidden" name="number" value="100" />
</form>
<script> document.getElementById('hacker-form').submit(); </script>
</body>
</html>
3. 引誘用戶點擊鏈接
<div>
<a href="https://www.test.com?user=hacker&number=100" taget="_blank">
點擊下載
</a>
</div>
與XSS不同,CSRF攻擊不需要將惡意代碼注入到用戶的頁面,僅僅是利用服務器的漏洞和用戶的登錄狀態來實施攻擊。發起CSRF攻擊有三個必要的條件:
- 目標站點一定要有CSRF漏洞;
- 用戶登陸過目標站點,並且在瀏覽器上保持有站點的登錄狀態;
- 用戶打開一個第三方站點,可以是黑客的惡意站點,也可以是一些論壇。
CSRF真實案例
如何防止CSRF攻擊
1. 利用好Cookie的SameSite屬性
想要進行CSRF攻擊就必須要有用戶的登錄狀態,大部分網站中Cookie都是瀏覽器與服務器之間維護登錄狀態的一個關鍵數據。並且大多數CSRF攻擊是需要從第三方站點發起,如果我們從第三方站點發送請求時禁止關鍵Cookie的發送,就可以有效降低CSRF攻擊的風險。
Cookie中的SameSite屬性正是爲了解決這個問題。SameSite有個值:
- Strict: 完全禁止第三方Cookie。
- Lax: 相對寬鬆。在跨站點(頂級域名+二級域名相同的爲同一站點)的情況下第三方站點的<a>標籤鏈接、Get方式的表單提交、預加載會攜帶Cookie。Post表單、ajax請求、img、iframe等都不會攜帶Cookie。
- None: 任何情況下都會攜帶Cookie。
兼容性問題:
大多數主流瀏覽器的最新版本中,默認值都爲Lax。如果想要設置爲None則必須要Secure一起使用,也就是說只能在https請求中使用None。
最新的safari瀏覽器中,默認值爲Strict,並且不支持None,設置爲None時會被當成默認值處理。
具體瀏覽器兼容版本查看 MDN SameSite Cookie。
2. 驗證請求來源
由於CSRF攻擊大多來自於第三方站點,所以我們可以通過判斷請求是否來自於第三方站點來過濾請求。
- Referer:Referer請求頭字段記錄了HTTP請求的來源地址。但是有些場景爲了安全考慮不適合把原站點的詳細路徑暴露給服務器,所以瀏覽器提供了設置關閉Referer值的上傳。
- Origin:由於驗證Referer屬性並不可靠,所以又制定了Origin屬性,Origin屬性只包括域名信息,沒有具體的URL路徑。
可以在使用CORS(跨站資源共享)時設置Access-Control-Allow-Origin響應頭來指示可以共享給哪些域。
3. CSRF Token
CSRF Token的實現方法有很多,目的都是爲了驗證請求是否是來自第三方。
介紹幾種常見的方式
- 響應頁面時將token渲染到頁面上,表單提交的時候通過隱藏域提交到服務端;
- 將token設置在Cookie中,在提交請求時將提交Cookie,並通過header或者body帶上Cookie中的token,服務端進行對比校驗;
- 信任帶有特定header的請求。(比較容易被繞過)
XST攻擊
XST全程是Cross-Site Tracing。雖然cookies設置爲httpOnly以後被XSS攻擊也無法輕易盜用cookie,但是針對支持TRACE請求的服務器,客戶端可以通過發送TRACE請求獲取到完整的頭信息。通過這種方式也是可以拿到設置爲httpOnly的Cookie。
可以通過禁止trace,track等危險請求類型來防範被攻擊。
現代瀏覽器已經禁止了trace、track梁總請求,會拋出異常。
HPP攻擊
Http Parameter Pollution(HPP),即 HTTP 參數污染攻擊。在 HTTP 協議中是允許同樣名稱的參數出現多次,而由於應用的實現不規範,攻擊者通過傳播參數的時候傳輸 key 相同而 value 不同的參數,從而達到繞過某些防護的後果。HPP 可能導致的安全威脅有:
- 繞過防護和參數校驗。
- 產生邏輯漏洞和報錯,影響應用代碼執行。
可以通過針對相同key的請求時,強制使用第一個或者最後一個來避免hpp攻擊。
HPP真實案例
SSRF攻擊
通過 Server-Side Request Forgery(SSRF)攻擊,攻擊者可以發起網絡請求訪問或者操作內部網絡的資源。
一般來說,SSRF 安全漏洞常見於開發者在服務端直接請求客戶端傳遞進來的 URL 資源,一旦攻擊者傳入一些內部的 URL 即可發起 SSRF 攻擊。
通常我們會基於內網 IP 黑名單的形式來防範 SSRF 攻擊,通過對解析域名後得到的 IP 做過濾,禁止訪問內部 IP 地址來達到防範 SSRF 攻擊的目的。