CSRF是什麼?
Cross-site request forgery 簡稱為“CSRF”,在CSRF的攻擊場景中攻擊者會偽造一個請求(這個請求一般是一個連結),然後欺騙目標使用者進行點選,使用者一旦點選了這個請求,整個攻擊就完成了。所以CSRF攻擊也成為"one click"攻擊。 很多人搞不清楚CSRF的概念,甚至有時候會將其和XSS混淆,更有甚者會將其和越權問題混為一談,這都是對原理沒搞清楚導致的。
簡單的說,是攻擊者透過一些技術手段欺騙使用者的瀏覽器去訪問一個自己以前認證過的站點並執行一些操作(如發郵件,發訊息,甚至財產操作(如轉賬和購買商品))。因為瀏覽器之前認證過,所以被訪問的站點會覺得這是真正的使用者操作而去執行。
Session工作原理
關於瀏覽器快取,cookie , session:http://www.cnblogs.com/yigeqi/p/6274602.html
理解Cookie和Session機制:https://www.cnblogs.com/andy-zhou/p/5360107.html
深入理解瀏覽器會話機制(session && cookie):https://blog.csdn.net/xi_2130/article/details/51361494
cookie 和session 的區別詳解:https://www.cnblogs.com/shiyangxt/articles/1305506.html
Cookie和Session詳解:https://blog.csdn.net/gaoyong_stone/article/details/79524321
如果我把瀏覽器的cookie禁用了,session還能正常工作嗎?
答案是否定的,我在這邊舉個簡單的例子幫助理解session。
比如我買了一張高爾夫俱樂部的會員卡,俱樂部給了我一張帶有卡號的會員卡。我能享受哪些權利(比如我是高階會員卡可以打19洞和後付費喝飲料,而初級會員卡只能在練習場揮杆)以及我的個人資料都是儲存在高爾夫俱樂部的資料庫裡的。我每次去高爾夫俱樂部只需要出示這張高階會員卡,俱樂部就知道我是誰了,並且為我服務了。
這裡我們的高階會員卡卡號 = 儲存在cookie的sessionid; 而我的高階會員卡權利和個人資訊就是服務端的session物件了。
我們知道http請求是無狀態的,也就是說每次http請求都是獨立的無關之前的操作的,但是每次http請求都會將本域下的所有cookie作為http請求頭的一部分傳送給服務端,所以服務端就根據請求中的cookie存放的sessionid去session物件中找到該會員資料了。
當然session的儲存方法多種多樣,可以儲存在檔案中,也可以記憶體裡,考慮到分散式的橫向擴充套件我們還是建議把它儲存在第三方媒介中,比如redis或者mongodb。
我們理解了session的工作機制後,CSRF也就很容易理解了。CSRF攻擊就相當於惡意使用者A複製了我的高階會員卡,哪天惡意使用者A也可以拿著這張假冒的高階會員卡去高爾夫俱樂部打19洞,享受美味的飲料了,而我在月底就會收到高爾夫俱樂部的賬單!
原理及過程
因為CSRF攻擊,會重複利用使用者的 Cookie,而說到 Cookie,就得先從 HTTP 協議開始講起。
HTTP協議
無記憶性
HTTP是一種無狀態協議,即伺服器不會保留與客戶交易時的狀態。
使用者A在很短的時間間隔內向Web伺服器傳送了兩次同樣的請求,伺服器並不會因為已經響應了該請求一次就不對第二次請求進行響應,因為伺服器並不知道已經響應過一次該請求。
假設使用者在網站A的某一個頁面上已經完成了登入操作,當在該網站的另一個頁面上執行的操作需要驗證使用者登入的時候仍然需要使用者再次登入,因為HTTP並不知道你已經登入了,它不會維持你的登入狀態。
因為要多次登入太過麻煩,為了讓伺服器能夠記住使用者引入了Cookle機制
Cookie 機制
當使用者訪問站點的時候,站點會為該使用者分配一個 Cookie 值,站點使用該 Cookie 值來標記使用者,當使用者瀏覽器接受到包含 Cookie 值得資料包後,會將 Cookie 得值取出,存放到瀏覽器中,隨後瀏覽器會在發往該站點得資料包中自動得填充該 Cookie 值。Cookie 的值的填充是瀏覽器的行為。
當瀏覽器自動完成Cookie的填充,目標網站會誤認為該資料包就是管理員傳送的,會以管理員的許可權進行相關的操作。
第一方和第三方cookie概念
Cookie是一個域伺服器儲存在瀏覽器中的一小段資料塊,只能被這個域訪問,誰設定則誰訪問。
第一方Cookie:比如,訪問www.a.com這個網站,這個網站設定了一個Cookie,這個Cookie也只能被www.a.com這個域下的網頁讀取。
第三方Cookie:比如,訪問www.a.com這個網站,網頁裡有用到www.b.com網站的一張圖片,瀏覽器在www.b.com請求圖片的時候,www.b.com設定了一個Cookie,那這個Cookie只能被www.b.com這個域訪問,反而不能被www.a.com這個域訪問,因為對我們來說,我們實際是在訪問www.a.com這個網站被設定了一個www.b.com這個域下的Cookie,所以叫第三方Cookie。
CSRF原理:
1、客戶端透過賬戶密碼登入訪問網站A。
2、網站A驗證客戶端的賬號密碼,成功則生成一個sessionlD,並返回給客戶端儲存在瀏覽器中。
3、該客戶端Tab—個新頁面訪問了網站B。
4、網站B自動觸發要求該客戶端訪問網站A。(即在網站B中有連結指向網站A)
5、客戶端透過網站B中的連結訪問網站A。(此時攜帶有合法的SessionID進行訪問站A的)
6、此時網站A只需檢驗sessionIlD是否合法,合法則執行相應的操作。(因此具體啥工具就得看連結,以及網站B要求訪問時攜帶的資料)
簡而言之: 透過訪問惡意網址,惡意網址返回來js自動執行訪問你之前登陸的網址,因為你已經登入了,所以再次訪問將會攜帶cookie,因為伺服器只認有沒有cookie,無法區分是不是使用者正常的訪問,所以會欺騙伺服器,造成傷害/
要完成一次CSRF攻擊,受害者必須依次完畢兩個步驟:
登入受信任站點A,並在本地生成Cookie。
在不登出A的情況下,訪問危急站點B。
站點 A 沒有做任何 CSRF 防禦
CSRF與XSS的區別
- CSRF:屬於業務邏輯漏洞,在伺服器看來,所有請求都是合法正常的
- XSS、SQL隱碼攻擊等:都是屬於技術漏洞
- XSS是基於客戶信任伺服器,而CSRF是基於伺服器信任客戶(經過身份驗證的)
場景需求:
小黑想要修改大白在購物網站www.xx.com上填寫的會員地址。
先看下大白是如何修改自己的密碼的:
登入---修改會員資訊,提交請求---修改成功。
所以小黑想要修改大白的資訊,他需要擁有:1,登入許可權 2,修改個人資訊的請求。
但是大白又不會把自己xxx網站的賬號密碼告訴小黑,那小黑怎麼辦?
於是他自己跑到www.xx.com上註冊了一個自己的賬號,然後修改了一下自己的個人資訊(比如:E-mail地址),他發現修改的請求是:
【http://www.xxx.com/edit.php?email=xiaohei@88.com&Change=Change】
於是,他實施了這樣一個操作:把這個連結偽裝一下,在小白登入xxx網站後,欺騙他進行點選,小白點選這個連結後,個人資訊就被修改了,小黑就完成了攻擊目的。
為啥小黑的操作能夠實現呢。有如下幾個關鍵點:
1.www.xxx.com這個網站在使用者修改個人的資訊時沒有過多的校驗,導致這個請求容易被偽造;
---因此,我們判斷一個網站是否存在CSRF漏洞,其實就是判斷其對關鍵資訊(比如密碼等敏感資訊)的操作(增刪改)是否容易被偽造。
2.小白點選了小黑髮給的連結,並且這個時候小白剛好登入在購物網上;
---如果小白安全意識高,不點選不明連結,則攻擊不會成功,又或者即使小白點選了連結,但小白此時並沒有登入購物網站,也不會成功。
---因此,要成功實施一次CSRF攻擊,需要“天時,地利,人和”的條件。
當然,如果小黑事先在xxx網的首頁如果發現了一個XSS漏洞,則小黑可能會這樣做: 欺騙小白訪問埋伏了XSS指令碼(盜取cookie的指令碼)的頁面,小白中招,小黑拿到小白的cookie,然後小黑順利登入到小白的後臺,小黑自己修改小白的相關資訊。
---所以跟上面比一下,就可以看出CSRF與XSS的區別:CSRF是借使用者的許可權完成攻擊,攻擊者並沒有拿到使用者的許可權,而XSS是直接盜取到了使用者的許可權,然後實施破壞。
因此,網站如果要防止CSRF攻擊,則需要對敏感資訊的操作實施對應的安全措施,防止這些操作出現被偽造的情況,從而導致CSRF。比如:
--對敏感資訊的操作增加安全的token;
--對敏感資訊的操作增加安全的驗證碼;
--對敏感資訊的操作實施安全的邏輯流程,比如修改密碼時,需要先校驗舊密碼等。
如何判斷有CSRF漏洞?
1、最簡單的方法就是抓取一個正常請求的資料包,如果沒有Referer欄位和token,那麼極有可能存在CSRF漏洞。
2、如果有Referer欄位,但是去掉Referer欄位後再重新提交,如果該提交還有效,那麼基本上可以確定存在CSRF漏洞。
3、隨著對CSRF漏洞研究的不斷深入,不斷湧現出一些專門針對CSRF漏洞進行檢測的工具,如CSRFTester,CSRF Request Builder等。以CSRFTester工具為例,CSRF漏洞檢測工具的測試原理如下:
使用CSRFTester進行測試時,首先需要抓取我們在瀏覽器中訪問過的所有連結以及所有的表單等資訊,然後透過在CSRFTester中修改相應的表單等資訊,重新提交,這相當於一次偽造客戶端請求。
如果修改後的測試請求成功被網站伺服器接受,則說明存在CSRF漏洞,當然此款工具也可以被用來進行CSRF攻擊。
如何防禦CSRF漏洞
CSRF攻擊防禦的重點是利用cookie的值只能被第一方讀取,無法讀取第三方的cookie值。
-
驗證碼; 驗證 HTTP Referer 欄位; 在請求地址中新增 token 並驗證; 在 HTTP 頭中自定義屬性並驗證;
驗證碼
驗證碼被認為是對抗CSRF攻擊最簡潔而有效的防禦方法。
CSRF攻擊的過程,往往是在使用者不知情的情況下構造了網路請求。而驗證碼,則強制使用者必須與應用進行互動,才能完成最終請求。因此在通常情況下,驗證碼能夠很好地遏制CSRF攻擊。
但是驗證碼並非萬能。很多時候,出於使用者體驗考慮,網站不能給所有的操作都加上驗證碼。因此,驗證碼只能作為防禦CSRF的一種輔助手段,而不能作為最主要的解決方案。
驗證 HTTP Referer 欄位
根據 HTTP 協議,在 HTTP 頭中有一個欄位叫Referer,它記錄了該 HTTP 請求的來源地址。在通常情況下,訪問一個安全受限頁面的請求來自於同一個網站,比如需要訪問 :
http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory
使用者必須先登陸 bank.example,然後透過點選頁面上的按鈕來觸發轉賬事件。
這時,該轉帳請求的 Referer 值就會是轉賬按鈕所在的頁面的 URL,通常是以 bank.example 域名開頭的地址。而如果駭客要對銀行網站實施 CSRF 攻擊,他只能在他自己的網站構造請求,當使用者透過駭客的網站傳送請求到銀行時,該請求的 Referer 是指向駭客自己的網站。
因此,要防禦 CSRF 攻擊,銀行網站只需要對於每一個轉賬請求驗證其 Referer 值,如果是以 bank.example 開頭的域名,則說明該請求是來自銀行網站自己的請求,是合法的。如果 Referer 是其他網站的話,則有可能是駭客的 CSRF 攻擊,拒絕該請求。
這種方法的顯而易見的好處就是簡單易行,網站的普通開發人員不需要操心 CSRF 的漏洞,只需要在最後給所有安全敏感的請求統一增加一個攔截器來檢查 Referer 的值就可以。特別是對於當前現有的系統,不需要改變當前系統的任何已有程式碼和邏輯,沒有風險,非常便捷。
然而,這種方法並非萬無一失。Referer 的值是由瀏覽器提供的,雖然 HTTP 協議上有明確的要求,但是每個瀏覽器對於 Referer 的具體實現可能有差別,並不能保證瀏覽器自身沒有安全漏洞。
使用驗證 Referer 值的方法,就是把安全性都依賴於第三方(即瀏覽器)來保障,從理論上來講,這樣並不安全。
事實上,對於某些瀏覽器,比如 IE6 或 FF2,目前已經有一些方法可以篡改 Referer 值。如果 bank.example 網站支援 IE6 瀏覽器,駭客完全可以把使用者瀏覽器的 Referer 值設為以 bank.example 域名開頭的地址,這樣就可以透過驗證,從而進行 CSRF 攻擊。
即便是使用最新的瀏覽器,駭客無法篡改 Referer 值,這種方法仍然有問題。因為 Referer 值會記錄下使用者的訪問來源,有些使用者認為這樣會侵犯到他們自己的隱私權,特別是有些組織擔心 Referer 值會把組織內網中的某些資訊洩露到外網中。
因此,使用者自己可以設定瀏覽器使其在傳送請求時不再提供 Referer。當他們正常訪問銀行網站時,網站會因為請求沒有 Referer 值而認為是 CSRF 攻擊,拒絕合法使用者的訪問。
在請求地址中新增token欄位並驗證
什麼是token?
作為計算機術語時,是“令牌”的意思。Token是服務端生成的一串字串,以作客戶端進行請求的一個令牌,當第一次登入後,伺服器生成一個Token便將此Token返回給客戶端,以後客戶端只需帶上這個Token前來請求資料即可,無需再次帶上使用者名稱和密碼
token產生背景
HTTP 是一種沒有狀態的協議,也就是它並不知道是誰是訪問應用。這裡我們把使用者看成是客戶端,客戶端使用使用者名稱還有密碼透過了身份驗證,不過下回這個客戶端再傳送請求時候,還得再驗證一下。
token用來幹嘛的?
使用token機制的身份驗證方法,在伺服器端不需要儲存使用者的登入記錄。
大概的流程:
1️⃣客戶端使用使用者名稱和密碼請求登入。
2️⃣服務端收到請求,驗證使用者名稱和密碼。
3️⃣驗證成功後,服務端會生成一個token,然後把這個token傳送給客戶端。
4️⃣客戶端收到token後把它儲存起來,可以放在cookie或者Local Storage(本地儲存)裡。
5️⃣客戶端每次向服務端傳送請求的時候都需要帶上服務端發給的token。
6️⃣服務端收到請求,然後去驗證客戶端請求裡面帶著token,如果驗證成功,就向客戶端返回請求的資料。
token是如何防禦CSRF攻擊的?
CSRF 攻擊之所以能夠成功,是因為駭客可以完全偽造使用者的請求,該請求中所有的使用者驗證資訊都是存在於 cookie 中,因此駭客可以在不知道這些驗證資訊的情況下直接利用使用者自己的 cookie 來透過安全驗證。
要抵禦 CSRF關鍵在於在請求中放入駭客所不能偽造的資訊,並且該資訊不存在於 cookie 之中。
可以在 HTTP 請求中以引數的形式加入一個隨機產生的 token,並在伺服器端建立一個攔截器來驗證這token,如果請求中沒有 token 或者 token 內容不正確,則認為可能是 CSRF 攻擊而拒絕該請求。
這種方法要比檢查 Referer要安全一些,token 可以在使用者登陸後產生並放於 session 之中,然後在每次請求時把 token 從 session 中拿出,與請求中的 token 進行比對,但這種方法的難點在於如何把 token 以引數的形式加入請求。
對於 GET 請求,token 將附在請求地址之後,這樣 URL 就變成 http://url?csrftoken=tokenvalue。 而對於 POST 請求來說,要在 form 的最後加上 ,這樣就把 token 以引數的形式加入請求了。
但是,在一個網站中,可以接受請求的地方非常多,要對於每一個請求都加上 token 是很麻煩的,並且很容易漏掉,通常使用的方法就是在每次頁面載入時,使用 javascript 遍歷整個 dom 樹,對於 dom 中所有的 a 和 form 標籤後加入 token。
這樣可以解決大部分的請求,但是對於在頁面載入之後動態生成的 html 程式碼,這種方法就沒有作用,還需要程式設計師在編碼時手動新增 token。
該方法還有一個缺點是難以保證 token 本身的安全。特別是在一些論壇之類支援使用者自己發表內容的網站,駭客可以在上面釋出自己個人網站的地址。由於系統也會在這個地址後面加上 token,駭客可以在自己的網站上得到這個 token,並馬上就可以發動 CSRF 攻擊。
為了避免這一點,系統可以在新增 token 的時候增加一個判斷,如果這個連結是鏈到自己本站的,就在後面新增 token,如果是通向外網則不加。
不過,即使這個 csrftoken 不以引數的形式附加在請求之中,駭客的網站也同樣可以透過 Referer 來得到這個 token 值以發動 CSRF 攻擊。這也是一些使用者喜歡手動關閉瀏覽器 Referer 功能的原因。
在 HTTP 頭中自定義屬性並驗證
這種方法也是使用 token 並進行驗證,和上一種方法不同的是,這裡並不是把 token 以引數的形式置於 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性裡。透過 XMLHttpRequest 這個類,可以一次性給所有該類請求加上 csrftoken 這個 HTTP 頭屬性,並把 token 值放入其中。
這樣解決了上種方法在請求中加入 token 的不便,同時,透過XMLHttpRequest 請求的地址不會被記錄到瀏覽器的位址列,也不用擔心 token 會透過 Referer 洩露到其他網站中去。
然而這種方法的侷限性非常大,XMLHttpRequest 請求通常用於 Ajax 方法中對於頁面區域性的非同步重新整理,並非所有的請求都適合用這個類來發起,而且透過該類請求得到的頁面不能被瀏覽器所記錄下,從而進行前進,後退,重新整理,收藏等操作,給使用者帶來不便。
另外,對於沒有進行 CSRF 防護的遺留系統來說,要採用這種方法來進行防護,要把所有請求都改為 XMLHttpRequest 請求,這樣幾乎是要重寫整個網站,這代價無疑是不能接受的。