跨站點請求僞造(Cross Site Request Forgery)是一種經典的網路攻擊方式,它一直是OWASP TOP 10之一,也被稱爲「One Click Attack」或者Session Riding,通常縮寫爲CSRF或者XSRF,是一種對網站的惡意利用。儘管聽起來和跨站指令碼攻擊很相似,但它與跨站指令碼攻擊非常不同,並且攻擊方式幾乎完全不一樣。CSRF與XSS最大的區別就在於,CSRF並沒有盜取cookie而是直接利用。跨站指令碼攻擊利用站點內的信任使用者,而CSRF則通過僞裝來自受信任使用者的請求來利用受信任的網站。與跨站指令碼攻擊相比,CSRF攻擊往往不大流行,因此對其進行防範的措施也相當稀少,另外對於CSRF的防範難度更大,所以被認爲比跨站指令碼攻擊更具危險性。
你這可以這麼理解CSRF攻擊:攻擊者盜用了你的身份,以你的名義發送惡意請求。CSRF能夠做的事情包括:以你名義發送郵件,發訊息,盜取你的賬號,甚至於購買商品,虛擬貨幣轉賬…造成的問題包括:個人隱私泄露以及財產安全。
想要深入理解CSRF的攻擊特性我們有必要瞭解一下網站Session的工作原理。
Session大家都不陌生,無論是用.net還是PHP開發過網站的程式設計師都肯定用過Session物件,然而Session它是如何工作的呢?如果我們把瀏覽器的Cookie禁用了,大家認爲Session還能正常工作嗎? 答案是否定的。
在這裏舉個簡單的例子幫助大家理解Session。比如我買了一張高爾夫俱樂部的會員卡,俱樂部給了我一張帶有卡號的會員卡。我能享受哪些權利呢?如果我是高階會員卡可以打19洞和後付費喝飲料,而初級會員卡只能在練習場揮桿。我的個人資料都是儲存在高爾夫俱樂部的數據庫裡,我每次去高爾夫俱樂部只需要出示這張高階會員卡,俱樂部就知道我是誰了,並且爲我服務了。 因此我們的高階會員卡卡號相當於儲存在Cookie的Sessionid;而我的高階會員卡權利和個人資訊就相當於伺服器端的Session物件。
我們知道Http是無狀態的協定,它不要求瀏覽器在每次請求中標明用戶端自己的身份,並且瀏覽器以及伺服器之間並沒有保持一個永續性的連線用於多個頁面之間的存取。爲了維持web應用程式狀態的問題,每次Http請求都會將本域下的所有Cookie作爲Http請求頭的一部分發送給伺服器端,伺服器端就可以根據請求中的Cookie所存放的Sessionid去Session物件中找到該會員資料了。
我們理解了Session的工作機制 機製後,CSRF也就很容易理解了。CSRF攻擊就相當於攻擊使用者複製了我的高階會員卡,然後攻擊使用者就可以拿着這張假冒的高階會員卡去高爾夫俱樂部打19洞,享受美味的飲料,而我這個受害者在月底就會收到高爾夫俱樂部的賬單。
CSRF攻擊過程有以下兩個重點:
受害者Bob 在銀行有一筆存款,通過對銀行的網站發送請求 「http://bank.example/withdraw?account=bob&amount=1000000&for=bob2」可以使 Bob把1000000塊的存款轉到Bob2的賬號下。通常情況下,該請求發送到網站後,伺服器會先驗證該請求是否來自一個合法的Session,並且該Session的使用者Bob已經成功登陸。駭客Hacker自己在該銀行也有賬戶,他知道上文中的URL可以把錢進行轉帳操作。Hacker可以自己發送一個請求給銀行:http://bank.example/withdraw?account=bob&amount=1000000&for=Hacker。但是這個請求來自Hacker而非 Bob,他不能通過安全認證,因此該請求不會起作用。
這時,Hacker想到使用CSRF的攻擊方式,他先自己做一個網站,在網站中放入如下程式碼: src=「http://bank.example/withdraw?account=bob&amount=1000000&for=Hacker」,並且通過廣告等誘使 Bob 來存取他的網站。當Bob存取該網站時,上述URL就會從Bob的瀏覽器發向銀行,而這個請求會附帶Bob瀏覽器中的 Cookie 一起發向銀行伺服器。大多數情況下,該請求會失敗,因爲他要求Bob的認證資訊。但是,如果Bob當時恰巧剛存取他的銀行後不久,他的瀏覽器與銀行網站之間的Session尚未過期,瀏覽器的Cookie之中含有Bob的認證資訊。這時,悲劇發生了,這個URL請求就會得到響應,錢將從Bob的賬號轉移到Hacker的賬號,而Bob當時毫不知情。等以後Bob發現賬戶錢少了,即使他去銀行查詢日誌,他也只能發現確實有一個來自於他本人的合法請求轉移了資金,沒有任何被攻擊的痕跡。而Hacker則可以拿到錢後逍遙法外。
檢測CSRF漏洞是一項比較繁瑣的工作,最簡單的方法就是抓取一個正常請求的數據包,去掉Referer欄位後再重新提交,如果該提交還有效,那麼基本上可以確定存在CSRF漏洞。
隨着對CSRF漏洞研究的不斷深入,不斷涌現出一些專門針對CSRF漏洞進行檢測的工具,如CSRFTester,CSRF Request Builder等。以CSRFTester工具爲例,CSRF漏洞檢測工具的測試原理如下:使用CSRFTester進行測試時,首先需要抓取我們在瀏覽器中存取過的所有鏈接以及所有的表單等資訊,然後通過在CSRFTester中修改相應的表單等資訊,重新提交,這相當於一次僞造用戶端請求。如果修改後的測試請求成功被網站伺服器接受,則說明存在CSRF漏洞,當然此款工具也可以被用來進行CSRF攻擊。
目前防禦 CSRF 攻擊主要有三種策略:驗證 HTTP Referer 欄位;在請求地址中新增token並驗證;在HTTP頭中自定義屬性並驗證。
根據 HTTP 協定,在HTTP頭中有一個欄位叫Referer,它記錄了該 HTTP 請求的來源地址。在通常情況下,存取一個安全受限頁面的請求來自於同一個網站,比如需要存取 http://bank.example/withdraw?account=bob&amount=1000000&for= Hacker,使用者必須先登陸bank.example,然後通過點選頁面上的按鈕來觸發轉賬事件。這時,該轉帳請求的Referer值就會是轉賬按鈕所在的頁面的URL,通常是以bank.example域名開頭的地址。而如果駭客要對銀行網站實施 CSRF 攻擊,他只能在他自己的網站構造請求,當使用者通過駭客的網站發送請求到銀行時,該請求的 Referer 是指向駭客自己的網站。因此,要防禦CSRF攻擊,銀行網站只需要對於每一個轉賬請求驗證其Referer值,如果是以bank.example開頭的域名,則說明該請求是來自銀行網站自己的請求,是合法的。如果Referer是其他網站的話,則有可能是駭客的CSRF攻擊,拒絕該請求。
這種方法的顯而易見的好處就是簡單易行,網站的普通開發人員不需要操心CSRF的漏洞,只需要在最後給所有安全敏感的請求統一增加一個攔截器來檢查Referer的值就可以。特別是對於當前現有的系統,不需要改變當前系統的任何已有程式碼和邏輯,沒有風險,非常便捷。
然而,這種方法並非萬無一失。Referer的值是由瀏覽器提供的,雖然 HTTP協定上有明確的要求,但是每個瀏覽器對於Referer的具體實現可能有差別,並不能保證瀏覽器自身沒有安全漏洞。使用驗證 Referer 值的方法,就是把安全性都依賴於第三方(即瀏覽器)來保障,從理論上來講,這樣並不安全。事實上,對於某些瀏覽器已經有一些方法可以篡改 Referer值。如果bank.example網站支援 IE6 瀏覽器,駭客完全可以把使用者瀏覽器的Referer值設爲以bank.example域名開頭的地址,這樣就可以通過驗證,從而進行 CSRF 攻擊。
即便是使用最新的瀏覽器,駭客無法篡改Referer值,這種方法仍然有問題。因爲Referer值會記錄下使用者的存取來源,有些使用者認爲這樣會侵犯到他們自己的隱私權,特別是有些組織擔心Referer值會把組織內網中的某些資訊泄露到外網中。因此,使用者自己可以設定瀏覽器使其在發送請求時不再提供 Referer。當他們正常存取銀行網站時,網站會因爲請求沒有Referer值而認爲是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功能的原因。
這種方法也是使用token並進行驗證,和上一種方法不同的是,這裏並不是把token以參數的形式置於HTTP請求之中,而是把它放到HTTP頭中自定義的屬性裡。通過 XMLHttpRequest這個類,可以一次性給所有該類請求加上 csrftoken這個HTTP頭屬性,並把token值放入其中。這樣解決了上種方法在請求中加入token的不便,同時,通過XMLHttpRequest請求的地址不會被記錄到瀏覽器的位址列,也不用擔心token會透過Referer泄露到其他網站中去。
然而這種方法的侷限性非常大。XMLHttpRequest 請求通常用於Ajax方法中對於頁面區域性的非同步重新整理,並非所有的請求都適合用這個類來發起,而且通過該類請求得到的頁面不能被瀏覽器所記錄下,從而進行前進,後退,重新整理,收藏等操作,給使用者帶來不便。另外,對於沒有進行CSRF防護的遺留系統來說,要採用這種方法來進行防護,要把所有請求都改爲 XMLHttpRequest 請求,這樣幾乎是要重寫整個網站,這代價無疑是不能接受的。
SSRF(Sever-Side Request Forgery,伺服器端請求僞造)是一種由攻擊者構造請求,由伺服器端發起請求的安全漏洞。一般情況下,SSRF攻擊的目標是外網無法存取的內部系統(正因爲請求是由伺服器端發起的,所以伺服器端能請求到與自身相連而與外網隔離的內部系統)。
SSRF形成的原因大都是由於伺服器端提供了從其他伺服器應用獲取數據的功能,且沒有對目標地址做過濾與限制。例如,駭客操作伺服器端從指定URL地址獲取網頁文字內容,載入指定地址的圖片等,利用的是服務點的請求僞造。SSRF利用存在缺陷的Web應用作爲代理攻擊遠端和原生的伺服器。
可以使用如下理解:
首先,我們要對目標網站的架構瞭解,腦子要有一個架構圖。比如 : A網站,是一個所有人都可以存取的外網網站,B網站是一個他們內部的OA網站。
所以,我們普通使用者只可以存取a網站,不能存取b網站。但是我們可以同過a網站做中間人,存取b網站,從而達到攻擊b網站需求。
正常使用者存取網站的流程是:
輸入A網站URL --> 發送請求 --> A伺服器接受請求(沒有過濾),並處理 -->返回使用者響應
【那網站有個請求是www.baidu,com/xxx.php?image=URL】
那麼產生SSRF漏洞的環節在哪裏呢?安全的網站應接收請求後,檢測請求的合法性
產生的原因:伺服器端的驗證並沒有對其請求獲取圖片的參數(image=)做出嚴格的過濾以及限制,導致A網站可以從其他伺服器的獲取數據
例如:www.baidu.com/xxx.php?image=www.abc.com/1.jpg
如果我們將www.abc.com/1.jpg換爲與該伺服器相連的內網伺服器地址會產生什麼效果呢?
如果存在該內網地址就會返回1xx 2xx 之類的狀態碼,不存在就會其他的狀態碼
終極簡析: SSRF漏洞就是通過篡改獲取資源的請求發送給伺服器,但是伺服器並沒有檢測這個請求是否合法的,然後伺服器以他的身份來存取其他伺服器的資源。
SSRF漏洞利用測試地址:http://127.0.0.1/web/ssrf.php?url=127.0.0.1/l.php
頁面ssrf.php實現的功能是獲取GET參數URL,然後將URL的內容返回網頁上。如果將請求的網址篡改爲http://www.baidu.com,則頁面會顯示http://www.baidu.com的網頁內容,如下所示:
但是,當設定參數URL爲內網地址時,則會泄露內網資訊,例如,當url=192.168.0.2:3306時,頁面返回如下:
說明存在mysql服務。
存取ssrf.php?url=file:///C:/windows/win.ini即可讀取本地檔案,如下所示:
在頁面SSRF.PHP中,程式獲取GET參數URL,通過curl_init()初始化curl元件後,將參數URL帶入curl_setopt($ch, CURLOPT_URL, $url),然後呼叫curl_exec請求該URL.由於伺服器端會將banner資訊返回用戶端,所以可以根據banner判斷主機是否存在某些服務,程式碼如下:
態。