CSRF
(跨站請求僞造)是一種挾制用戶在當前已登陸的Web
應用程序上執行非本意的操做的攻擊方法。javascript
跨站請求攻擊,簡單地說,是攻擊者經過一些技術手段欺騙用戶的瀏覽器去訪問一個本身曾經認證過的網站並運行一些操做(如發郵件,發消息,甚至財產操做如轉帳和購買商品)。這個過程能夠這樣來看:php
sueRimn.com
,並保留了登陸憑證(Cookie
)sb.com
(用戶自身不知道)sb.com
向sue.com
發送請求:sue.com/act=xx
,瀏覽器會默認攜帶sue.com
的cookie
sue.com
接收到請求後,對請求進行驗證,發現是曾經用戶保留的cookie
,誤覺得是用戶本人操做,而後容許了執行請求可見完成一次CSRF
攻擊,用戶必須依次存在着兩個操做:html
Cookie
因爲瀏覽器曾經認證過,因此被訪問的網站會認爲是真正的用戶操做而去運行。這利用了web中用戶身份驗證的一個漏洞:簡單的身份驗證只能保證請求發自某個用戶的瀏覽器,卻不能保證請求自己是用戶自願發出的。前端
攻擊者並不能經過CSRF攻擊來直接獲取用戶的帳戶控制權,也不能直接竊取用戶的任何信息。他們能作到的,是欺騙用戶瀏覽器,讓其以用戶的名義運行操做。java
CSRF
特色URL
、超連接、CORS
、Form
提交等等。部分請求方式能夠直接嵌入在第三方論壇、文章中,難以進行追蹤CSRF
一般是跨域的,由於外域一般更容易被攻擊者掌控。可是若是本域下有容易被利用的功能,好比能夠發圖和連接的論壇和評論區,攻擊能夠直接在本域下進行,並且這種攻擊更加危險。git
CSRF
攻擊類型GET
類型的CSRF
利用很是簡單,只須要一個HTTP
請求,通常會這樣利用:github
<img src="http://bank.example/withdraw?amount=10000&for=hacker" >
在受害者訪問含有這個img
的頁面後,瀏覽器會自動向http://bank.example/withdraw?account=xiaoming&amount=10000&for=hacker
發出一次HTTP請求。bank.example
就會收到包含受害者登陸信息的一次跨域請求。web
這種類型的CSRF利用起來一般使用的是一個自動提交的表單,如:面試
<form action="http://bank.example/withdraw" method=POST> <input type="hidden" name="account" value="xiaoming" /> <input type="hidden" name="amount" value="10000" /> <input type="hidden" name="for" value="hacker" /> </form> <script> document.forms[0].submit(); </script>
訪問該頁面後,表單會自動提交,至關於模擬用戶完成了一次POST
操做。數據庫
POST
類型的攻擊一般比GET
要求更加嚴格一點,但仍並不複雜。任何我的網站、博客,被黑客上傳頁面的網站都有多是發起攻擊的來源,後端接口不能將安全寄託在僅容許POST
上面。
連接類型的CSRF
並不常見,比起其餘兩種用戶打開頁面就中招的狀況,這種須要用戶點擊連接纔會觸發。這種類型一般是在論壇中發佈的圖片中嵌入惡意連接,或者以廣告的形式誘導用戶中招,攻擊者一般會以比較誇張的詞語誘騙用戶點擊,例如:
<a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank"> 重磅消息!! <a/>
因爲以前用戶登陸了信任的網站A,而且保存登陸狀態,只要用戶主動訪問上面的這個PHP
頁面,則表示攻擊成功。
CSRF
一般從第三方網站發起,被攻擊的網站沒法防止攻擊發生,只能經過加強本身網站針對CSRF
的防禦能力來提高安全性。
上文中講了CSRF
的兩個特色:
CSRF
(一般)發生在第三方域名CSRF
攻擊者不能獲取到Cookie
等信息,只是使用針對以上兩個特色制定兩個反向的防護措施,以下:
主要的方式有:
Samesite Cookie
既然CSRF
大多來自第三方網站,那麼咱們就直接禁止外域(或者不受信任的域名)對咱們發起請求。
那麼問題來了,咱們如何判斷請求是否來自外域呢?
在HTTP
協議中,每個異步請求都會攜帶兩個Header
,用於標記來源域名:
Origin Header
Referer Header
這兩個Header
在瀏覽器發起請求時,大多數狀況會自動帶上,而且不能由前端自定義內容。 服務器能夠經過解析這兩個Header
中的域名,肯定請求的來源域。
使用Origin Header肯定來源域名
在部分與CSRF
有關的請求中,請求的Header
中會攜帶Origin
字段。字段內包含請求的域名(不包含path
及query
)。
若是Origin
存在,那麼直接使用Origin
中的字段確認來源域名就能夠。
可是Origin
在如下兩種狀況下並不存在:
檢查Referer
字段
HTTP
頭中有一個Referer
字段,這個字段用以標明請求來源於哪一個地址。在處理敏感數據請求時,一般來講,Referer
字段應和請求的地址位於同一域名下。
這種辦法簡單易行,工做量低,僅須要在關鍵訪問處增長一步校驗。但這種辦法也有其侷限性,因其徹底依賴瀏覽器發送正確的Referer
字段。雖然http協議對此字段的內容有明確的規定,但並沒有法保證來訪的瀏覽器的具體實現,亦沒法保證瀏覽器沒有安全漏洞影響到此字段。而且也存在攻擊者攻擊某些瀏覽器,篡改其Referer
字段的可能。
設置Referrer Policy
的方法有三種:
CSP
設置meta
標籤a
標籤增長referrerpolicy
屬性上面說的這些比較多,但咱們能夠知道一個問題:攻擊者能夠在本身的請求中隱藏Referer。若是攻擊者將本身的請求這樣填寫,這個請求發起的攻擊將不攜帶Referer
:
![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2018b/ff0cdbee.example/withdraw?amount=10000&for=hacker)
另外在如下狀況下Referer
沒有或者不可信:
IE六、7
下使用window.location.href=url
進行界面的跳轉,會丟失Referer
IE六、7
下使用window.open
,也會缺失Referer
HTTPS
頁面跳轉到HTTP
頁面,全部瀏覽器Referer
都丟失Flash
上到達另一個網站的時候,Referer
的狀況就比較雜亂,不太可信綜上所述:同源驗證是一個相對簡單的防範方法,可以防範絕大多數的CSRF
攻擊。但這並非萬無一失的,對於安全性要求較高,或者有較多用戶輸入內容的網站,咱們就要對關鍵的接口作額外的防禦措施。
Samesite Cookie
屬性爲了從源頭上解決這個問題,Google
起草了一份草案來改進HTTP
協議,那就是爲Set-Cookie
響應頭新增Samesite
屬性,它用來標明這個 Cookie
是個「同站 Cookie
",同站Cookie
只能做爲第一方Cookie
,不能做爲第三方Cookie,Samesite
有兩個屬性值,分別是 Strict
和 Lax
token
因爲CSRF
的本質在於攻擊者欺騙用戶去訪問本身設置的地址,因此若是要求在訪問敏感數據請求時,要求用戶瀏覽器提供不保存在cookie中,而且攻擊者沒法僞造的數據做爲校驗,那麼攻擊者就沒法再運行CSRF
攻擊。
CSRF Token
的防禦策略分爲三個步驟:
CSRF Token
輸出到頁面中Token
Token
是否正確Cookie
驗證在會話中存儲CSRF Token
比較繁瑣,並且不能在通用的攔截上統一處理全部的接口。
那麼另外一種防護措施是使用雙重提交Cookie
。利用CSRF
攻擊不能獲取到用戶Cookie
的特色,咱們能夠要求Ajax
和表單請求攜帶一個Cookie
中的值。
雙重Cookie
採用如下流程:
Cookie
,內容爲隨機字符串(例如csrfcookie=v8g9e4ksfhw
)。Cookie
,並添加到URL
的參數中(接上例POST https://www.a.com/comment?csrfcookie=v8g9e4ksfhw
)。Cookie
中的字段與URL
參數中的字段是否一致,不一致則拒絕。雙重Cookie防護CSRF的優缺點:
優勢:
Session
,適用面更廣,易於實施Token
儲存於客戶端中,不會給服務器帶來壓力Token
,實施成本更低,能夠在先後端統一攔截校驗,而不須要一個個接口和頁面添加缺點:
Cookie
中增長了額外的字段XSS
),攻擊者能夠注入Cookie
,那麼該防護方式失效Cookie
傳輸安全,採用這種防護方式的最好確保用整站HTTPS
的方式,若是還沒切HTTPS
的使用這種方式也會有風險CSRF
測試步驟:
CSRF
修改並僞造請求CSRF
監控CSRF攻擊有着比較明顯的特徵:
在網站的代理層監控全部的接口請求,若是請求符合上面的特徵,就能夠認爲請求有CSRF
攻擊嫌疑。咱們能夠提醒對應的頁面和項目負責人,檢查或者 Review
其CSRF
防禦策略。
CSRF
攻擊簡單總結CSRF
自動防護策略:同源檢測(Origin
和 Referer
驗證)CSRF
主動防護措施:Token
驗證 或者 雙重Cookie
驗證 以及配合Samesite Cookie
GET
頁面中作用戶操做XSS
(跨站腳本攻擊)XSS
攻擊是指在用戶在訪問信任的網站時注入惡意攻擊腳本(主要是JavaScript
),對客戶端網頁進行篡改,對用戶瀏覽器進行控制或者獲取用戶隱私數據的一種攻擊方式。
攻擊者對客戶端網頁注入的惡意腳本通常包括 JavaScript
,有時也會包含 HTML
和 Flash
。有不少種方式進行 XSS
攻擊,但它們的共同點爲:獲取用戶隱私數據像 cookie
、session
,進行一些惡意操做。
用戶是經過哪一種方法「注入」惡意腳本的呢?
不只僅是業務上的「用戶的 UGC
內容」能夠進行注入,包括URL
上的參數等均可以是攻擊的來源。在處理輸入時,如下內容都不可信:
UGC
信息URL
參數POST
參數Referer
(可能來自不可信的來源)Cookie
(可能來自其餘子域注入)XSS
注入方式 HTML
中內嵌的文本中,惡意內容以 script
標籤造成注入。 JavaScript
中,拼接的數據突破了本來的限制(字符串,變量,方法名等)。href
、src
等屬性中,包含 javascript:
等可執行代碼。onload
、onerror
、onclick
等事件中,注入不受控制代碼。 style
屬性和標籤中,包含相似 background-image:url("javascript:...");
的代碼(新版本瀏覽器已經能夠防範)。expression(...)
的 CSS 表達式代碼(新版本瀏覽器已經能夠防範)。總之,若是開發者沒有將用戶輸入的文本進行合適的過濾,就貿然插入到 HTML
中,這很容易形成注入漏洞。攻擊者能夠利用漏洞,構造出惡意的代碼指令,進而利用惡意代碼危害數據安全。
XSS
攻擊的分類根據攻擊的來源,XSS
攻擊可分爲存儲型、反射型和 DOM
型三種。
XSS
存儲型 XSS 的攻擊步驟:
這種攻擊常見於帶有用戶保存數據的網站功能,如論壇發帖、商品評論、用戶私信等。
XSS
反射型 XSS
的攻擊步驟:
URL
,其中包含惡意代碼。 URL
時,網站服務端將惡意代碼從 URL
中取出,拼接在 HTML
中返回給瀏覽器。反射型 XSS
跟存儲型 XSS
的區別是:存儲型 XSS
的惡意代碼存在數據庫裏,反射型 XSS
的惡意代碼存在 URL
裏。
反射型 XSS
漏洞常見於經過 URL
傳遞參數的功能,如網站搜索、跳轉等。
因爲須要用戶主動打開惡意的 URL
才能生效,攻擊者每每會結合多種手段誘導用戶點擊。
POST
的內容也能夠觸發反射型 XSS
,只不過其觸發條件比較苛刻(須要構造表單提交頁面,並引導用戶點擊),因此很是少見。
DOM
型XSS
DOM
型 XSS
的攻擊步驟:
URL
,其中包含惡意代碼。 URL
。JavaScript
取出 URL
中的惡意代碼並執行。DOM
型 XSS
跟前兩種 XSS
的區別:DOM
型 XSS
攻擊中,取出和執行惡意代碼由瀏覽器端完成,不須要服務器解析響應的直接參與,屬於前端 JavaScript
自身的安全漏洞,而其餘兩種 XSS
都屬於服務端的安全漏洞。
對比表格
XSS 攻擊類型 |
存儲位置 | 插入點 |
---|---|---|
存儲型XSS |
後端數據庫 | HTML |
反射型XSS |
URL |
HTML |
DOM 型XSS |
後端數據庫/前端存儲/URL |
前端JavaScript |
XSS
攻擊的防護措施只要有輸入數據的地方,就存在XSS
攻擊,XSS
攻擊就有兩大因素:
因此,防護措施主要從這兩個方面下手:
針對用戶輸入過程
在用戶輸入過程當中,前端過濾輸入的惡意代碼,而後再提交給後端,這是不可行的,攻擊者徹底能夠繞過前端過濾,直接構造請求提交惡意代碼。
對於明確的輸入類型,例如數字、URL、電話號碼、郵件地址等等內容,進行輸入過濾是必要的。
因此不只須要前端對輸入的格式進行格式檢查,而後後端作相關的過濾檢查。
針對瀏覽器執行惡意代碼
輸入過濾不是萬全之策,那就要防止瀏覽器執行惡意代碼,這個分爲兩步進行:
HTML
中出現注入JavaScript
執行時,執行惡意代碼XSS
攻擊存儲型和反射型 XSS
都是在服務端取出惡意代碼後,插入到響應 HTML
裏的,攻擊者刻意編寫的「數據」被內嵌到「代碼」中,被瀏覽器所執行。
預防這兩種漏洞,有兩種常見作法:
HTML
純前端渲染的過程:
HTML
,此 HTML
中不包含任何跟業務相關的數據HTML
中的 JavaScript
JavaScript
經過 Ajax
加載業務數據,調用 DOM API
更新到頁面上在純前端渲染中,會明確的告訴瀏覽器:下面要設置的內容是文本(.innerText
),仍是屬性(.setAttribute
),仍是樣式(.style
)等等。瀏覽器不會被輕易的被欺騙,執行預期外的代碼了。
但純前端渲染還需注意避免 DOM
型 XSS
漏洞(例如 onload
事件和 href
中的 javascript:xxx
等,請參考下文」預防 DOM
型 XSS
攻擊「部分)。
在不少內部、管理系統中,採用純前端渲染是很是合適的。但對於性能要求高,或有 SEO
需求的頁面,咱們仍然要面對拼接 HTML
的問題。
HTML
若是拼接 HTML
是必要的,就須要採用合適的轉義庫,對 HTML
模板各處插入點進行充分的轉義。
經常使用的模板引擎,如 doT.js
、ejs
、FreeMarker
等,對於 HTML
轉義一般只有一個規則,就是把 & < > " ' /
這幾個字符轉義掉,確實能起到必定的 XSS
防禦做用,但並不完善:
|XSS 安全漏洞|簡單轉義是否有防禦做用| |-|-| |HTML 標籤文字內容|有| |HTML 屬性值|有| |CSS 內聯樣式|無| |內聯 JavaScript|無| |內聯 JSON|無| |跳轉連接|無|
因此要完善 XSS
防禦措施,咱們要使用更完善更細緻的轉義策略。
DOM
型XSS
攻擊DOM
型 XSS
攻擊,實際上就是網站前端 JavaScript
代碼自己不夠嚴謹,把不可信的數據看成代碼執行了。
在使用 .innerHTML
、.outerHTML
、document.write()
時要特別當心,不要把不可信的數據做爲 HTML 插到頁面上,而應儘可能使用 .textContent
、.setAttribute()
等。
若是用 Vue/React
技術棧,而且不使用 v-html
/dangerouslySetInnerHTML
功能,就在前端 render 階段避免 innerHTML
、outerHTML
的 XSS
隱患。
DOM
中的內聯事件監聽器,如 location
、onclick
、onerror
、onload
、onmouseover
等,<a>
標籤的 href
屬性,JavaScript 的 eval()
、setTimeout()
、setInterval()
等,都能把字符串做爲代碼運行。若是不可信的數據拼接到字符串中傳遞給這些 API,很容易產生安全隱患,請務必避免。
<!-- 內聯事件監聽器中包含惡意代碼 --> ![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2018b/3e724ce0.data:image/png,) <!-- 連接內包含惡意代碼 --> <a href="UNTRUSTED">1</a>
<script> // setTimeout()/setInterval() 中調用惡意代碼 setTimeout("UNTRUSTED") setInterval("UNTRUSTED") // location 調用惡意代碼 location.href = 'UNTRUSTED' // eval() 中調用惡意代碼 eval("UNTRUSTED") </script>
雖然在渲染頁面和執行 JavaScript
時,經過謹慎的轉義能夠防止 XSS
的發生,但徹底依靠開發的謹慎仍然是不夠的。如下介紹一些通用的方案,能夠下降 XSS
帶來的風險和後果。
Content Security Policy
嚴格的 CSP
在 XSS
的防範中能夠起到如下的做用:
GitHub
使用)。Google Map
移動版在使用)。XSS
,利於儘快修復問題。對於不受信任的輸入,都應該限定一個合理的長度。雖然沒法徹底防止 XSS
發生,但能夠增長 XSS
攻擊的難度。
HTTP-only Cookie
: 禁止 JavaScript 讀取某些敏感 Cookie,攻擊者完成 XSS 注入後也沒法竊取此 Cookie
XSS
攻擊檢測兩種方法:
XSS
攻擊字符串手動檢測 XSS
漏洞jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e
這個字符串可以檢測到存在於 HTML
屬性、HTML
文字內容、HTML
註釋、跳轉連接、內聯 JavaScript
字符串、內聯 CSS
樣式表等多種上下文中的 XSS
漏洞,也能檢測 eval()
、setTimeout()
、setInterval()
、Function()
、innerHTML
、document.write()
等 DOM
型 XSS
漏洞,而且能繞過一些 XSS
過濾器。
只要在網站的各輸入框中提交這個字符串,或者把它拼接到 URL 參數上,就能夠進行檢測了。
http://xxx/search?keyword=jaVasCript%3A%2F*-%2F*%60%2F*%60%2F*%27%2F*%22%2F**%2F(%2F*%20*%2FoNcliCk%3Dalert()%20)%2F%2F%250D%250A%250d%250a%2F%2F%3C%2FstYle%2F%3C%2FtitLe%2F%3C%2FteXtarEa%2F%3C%2FscRipt%2F--!%3E%3CsVg%2F%3CsVg%2FoNloAd%3Dalert()%2F%2F%3E%3E
XSS
漏洞常見自動掃描工具尋找 XSS 漏洞,例如 Arachni、Mozilla HTTP Observatory、w3af 等
XSS
攻擊簡單總結XSS
是後端的責任。而 DOM
型 XSS
是前端 的責任。防護XSS
是須要後端和前端共同參與的系統工程HTML
時進行,而不是在提交用戶輸入時 HTML
屬性、HTML
文字內容、HTML
註釋、跳轉連接、內聯 JavaScript
字符串、內聯 CSS
樣式表等,所須要的轉義規則不一致。 業務 須要選取合適的轉義庫,並針對不一樣的上下文調用不一樣的轉義規則總結如下原則減小漏洞的產生:
利用模板引擎 :開啓模板引擎自帶的 HTML
轉義功能。例如:
ejs
中,儘可能使用 <%= data %>
而不是 <%- data %>
doT.js
中,儘可能使用 {{! data }
而不是 {{= data }
FreeMarker
中,確保引擎版本高於 2.3.24,而且選擇正確的 freemarker.core.OutputFormat
onLoad="onload('{{data}}')"
、onClick="go('{{action}}')"
這種拼接內聯事件的寫法。在 JavaScript
中經過 .addEventlistener()
事件綁定會更安全HTML
的方法比較危險,若是框架容許,使用 createElement
、setAttribute
之類的方法實現。或者採用比較成熟的渲染框架,如 Vue/React
等DOM
屬性、連接等位置時,要打起精神,嚴加防範 CSP
、輸入長度配置、接口安全措施等方法,增長攻擊的難度,下降攻擊的後果 XSS
攻擊字符串和自動掃描工具尋找潛在的 XSS
漏洞XSS
利用的是用戶對網站的信任,CSRF
利用的是網站對用戶網頁瀏覽器的信任。
參考文章: