CSRF(跨站請求偽造)攻擊
CSRF(Cross Site Request Forgery,跨站請求偽造)是一種近年來才逐漸被大眾了解的網絡攻擊方式,又被稱為One-Click Attack或Session Riding。
攻擊原理
CSRF攻擊的大致方式如下:某用戶登錄了A網站,認證信息保存在cookie中。當用戶訪問攻擊者創建的B網站時,攻擊者通過在B網站發送一個偽造的請求提交到A網站服務器上,讓A網站服務器誤以為請求來自於自己的網站,於是執行響應的操作,該用戶的信息邊遭到了篡改。總結起來就是,攻擊者利用用戶在瀏覽器中保存的認證信息,向對應的站點發送偽造請求。用戶的認證是通過保存在cookie中的數據實現,在發送請求是,只要瀏覽器中保存了對應的cookie,服務器端就會認為用戶已經處於登錄狀態,而攻擊者正是利用了這一機制。
攻擊示例
假設我們網站是一個社交網站(example.com),簡稱網站A,攻擊者的網站可以是任意類型的網站,在我們的網站中,刪除賬戶的操作通過GET請求執行,使用下面的視圖處理:
@app.route('/account/delete') def delete_account(): if not current_user.authenticated: abort(401) else: current_user.delete() return 'Deleted'
當用戶登錄后,只要訪問http://example.com/account/delete就會刪除賬戶。那么在攻擊者的網站上,只要創建一個顯示圖片的img標簽,其中的src屬性加入刪除賬戶的URL:
<img src=”http://example.com/account/delete”>
當用戶訪問B網站時,瀏覽器在解析網頁時會自動向img標簽的src屬性中的地址發送請求。此時你在A網站的登錄信息保存在cookie中,因此,僅僅是訪問B網站的頁面就會讓你的賬戶被刪除掉。
當然,現實中很少有網站會使用GET請求來執行包含數據更改的敏感操作,這里只是一個示例。
現在,假設我們吸取了教訓,改用POST請求提交刪除賬戶的請求。盡管如此,攻擊者只需要在B網站中內嵌一個隱藏表單,然后設置在頁面加載后執行提交表單的javaScript函數,攻擊仍然會在用戶訪問B網站時發起。
防范措施
正確使用HTTP方法
防范CSRF的基礎就是正確使用HTTP方法。在普通的Web程序中,一般只會用到GET和POST方法。而且目前在HTML中僅支持GET和POST方法(借助AJAX則可以使用其他方法)。在使用HTTP方法時,通常應該遵循下面 的原則:
1、 GET方法輸入安全方法,不會改變資源狀態,僅用於獲取資源。頁面中所有可以通過鏈接發起的請求都屬於GET請求。
2、 POST方法用戶創建、修改和刪除資源。在HTML中使用form標簽創建表單並設置提交方法為POST,在提交時會創建POST請求。
在GET請求中,查詢參數用來傳入過濾返回的資源,但是在某些情況下,也可以通過查詢參數傳遞少量非敏感信息。
在刪除資源時,應該將刪除按鈕內嵌在使用了POST方法的form元素中
CSRF令牌校驗
當處理非GET請求時,要想避免CSRF攻擊,關鍵在判斷請求是否來自自己的網站。理論上講,通過HTTP referrer可以判斷原站點從而避免CSRF攻擊,但是referer很容易被修改和偽造,所以不能作為主要的防御措施。
除了在表單中加入校驗碼外,一般的做法是通過在客戶端頁面中加入偽隨機數來防御CSRF攻擊,這個偽隨機數通過被稱為CSRF令牌(token)。
在計算機語境中,令牌(token)指用於標記、驗證和傳遞信息的字符,通常是通過一定算法生成的隨機數。
在HTML中,POST方法的請求通過表單創建。我們把在服務器端創建的偽隨機數(CSRF令牌)添加到表單中的隱藏字段里和session變量(即簽名cookie)中,當用戶提交表單時,這個令牌會和表單數據一起提交。在服務器端處理POST請求時,會對表單中的令牌值進行驗證,如果表單中的令牌值和seesion中的令牌值相同,就說明請求來自自己的網站。因為CSRF令牌在用戶向包含表單的頁面發起GET請求時創建,並且在一定時間內過期,一般情況下攻擊者無法獲取到這個令牌值,所以我們可以有效地區分出請求的來源是否安全。
對於AJAX請求,我們可以在XMLHttpRequest請求首部添加一個自定義字段X-CSRFToken來保存CSRF令牌。
如果程序存在XSS漏洞,那么攻擊者可以使用javaScript竊取cookie內容,進而獲取CSRF令牌。