天天看點

Django架構之跨站請求僞造

本文介紹Django架構之跨站請求僞造

跨站請求僞造(英語:Cross-site request forgery),也被稱為one-click attack或者session riding,通常縮寫為CSRF或者XSRF, 是一種挾制使用者在目前已登入的Web應用程式上執行非本意的操作的攻擊方法。

跟跨網站腳本(XSS)相比,XSS利用的是使用者對指定網站的信任,CSRF 利用的是網站對使用者網頁浏覽器的信任。

例如:

如果使用者登入網絡銀行去檢視其存款餘額,他沒有退出網絡銀行系統就去了自己喜歡的論壇去灌水,如果攻擊者在論壇中精心構造了一個惡意的連結并誘使該使用者點選了該連結,那麼該使用者在網絡銀行帳戶中的資金就有可能被轉移到攻擊者指定的帳戶中。

造成CSRF的原因

跨站請求僞造能否得逞,與以下幾個方面密不可分,分别是浏覽器對會話的處理,攻擊者對Web應用有關URL的了解,應用程式賴以管理會話的資訊對浏覽器的透明性以及各種能夠引發資源請求HTML标簽等。

正常:
a. 先GET請求:
    頁面
    隐藏:input 随機字元串
b. POST
    資料
    input 随機字元串

非法:
b. POST
    資料
           

首先,我們來了解一些Web浏覽器對于Cookie和HTTP身份驗證資訊之類的會話資訊的處理方式。目前,浏覽器會自動地發送辨別使用者對話的資訊,而無需使用者幹預,換句話說,當浏覽器發送這些身份資訊的時候,使用者根本感覺不到。假設站點A上有一個Web應用程式,并且受害者正好已經在該站點上通過了身份認證,這時,站點會向受害者發送一個cookie作為響應,這個cookie的作用是什麼呢?主要是被站點作為使用者會話的标志,即如果站點收到了帶有受害者的cookie的請求,那麼它就會把這個請求看作是已登入的受害者發來的。一般情況下,浏覽器收到站點設定的cookie之後,每當向該站點發送請求的時候,浏覽器都會“自動地”連同該cookie一起發出。

然後,我們再來讨論一下攻擊者對Web應用程式URL的了解。如果應用程式沒有在URL中使用跟會話有關的資訊的話,那麼通過代碼分析或者通過通路該應用程式并檢視嵌入HTML/JavaScript中的URL以及表單來了解應用程式有關的URL、參數和容許值。

接下來,我們讨論一下應用程式賴以管理會話的資訊對浏覽器的透明性問題。我們知道,為了提高Web應用的便利性,用來管理會話的資訊,例如Cookie或者基于HTTP的身份驗證(例如HTTP基本認證、非基于表單的認證)等敏感資訊,都是由浏覽器來存放的,并在每當向需要身份驗證的應用程式發送請求時自動捎帶上這些資訊。也就是說,浏覽器可以通路會話管理資訊,如果Web應用程式完全依賴于這類資訊來識别一個使用者會話,這就為跨站請求僞造創造了條件。

上面所說的三個因素,是跨站請求僞造攻擊的必要的條件,而下面所說的,是一個“錦上添花”的因素,即沒有它也能發動跨站請求僞造攻擊,但是有了它能使該攻擊更加容易。這就是存在多種HTML标簽,如果頁面内出現這些标簽,會立刻引起浏覽器對http[s]資源的通路,例如圖像标簽img便是其中之一。

在Django中

django為使用者實作防止跨站請求僞造的功能,通過中間件 django.middleware.csrf.CsrfViewMiddleware 來完成。而對于django中設定防跨站請求僞造功能有分為全局和局部。

全局:

中間件 django.middleware.csrf.CsrfViewMiddleware

局部:

  • @csrf_protect,為目前函數強制設定防跨站請求僞造功能,即便settings中沒有設定全局中間件。
  • @csrf_exempt,取消目前函數防跨站請求僞造功能,即便settings中設定了全局中間件。

注:from django.views.decorators.csrf import csrf_exempt,csrf_protect

應用

1、普通表單

veiw中設定傳回值:
  return render_to_response('Account/Login.html',data,context_instance=RequestContext(request))  
     或者
     return render(request, 'xxx.html', data)
  
html中設定Token:
  {% csrf_token %}
           

Form送出:

<form action="/icbc.html" method="POST">
    {% csrf_token %}
    <input type='text' name='from'  />
    <input type='text' name='to' />
    <input type='text'  name='money' />
    <input type='submit' value='轉賬' />
           

2、Ajax

對于傳統的form,可以通過表單的方式将token再次發送到服務端,而對于ajax的話,使用如下方式。

view.py

from django.template.context import RequestContext
# Create your views here.
  
def test(request):
  
    if request.method == 'POST':
        print request.POST
        return HttpResponse('ok')
    return  render_to_response('app01/test.html',context_instance=RequestContext(request))
           

text.html

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    {% csrf_token %}
  
    <input type="button" onclick="Do();"  value="Do it"/>
  
    <script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
    <script src="/static/plugin/jquery/jquery.cookie.js"></script>
    <script type="text/javascript">
        var csrftoken = $.cookie('csrftoken');
  
        function csrfSafeMethod(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }
        $.ajaxSetup({
            beforeSend: function(xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            }
        });
        function Do(){
  
            $.ajax({
                url:"/app01/test/",
                data:{id:1},
                type:'POST',
                success:function(data){
                    console.log(data);
                }
            });
  
        }
    </script>
</body>
</html>
           

Ajax送出:

1)基于請求體:

function ajaxSubmit() {
	$.ajax({
		url: "/icbc.html",
		type: 'POST',
		data: {'k1':'v1','k2':'v2','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val() },
		success:function (arg) {
			console.log(arg)
		}
	})
}

// 隻能寫在模闆中
function ajaxSubmit() {
	$.ajax({
		url: "/icbc.html",
		type: 'POST',
		data: {'k1':'v1','k2':'v2','csrfmiddlewaretoken':"{{ csrf_token }}" },
		success:function (arg) {
			console.log(arg)
		}
	})
}

function ajaxSubmit() {
	$.ajax({
		url: "/icbc.html",
		type: 'POST',
		data: $('#f1').serialize(),
		success:function (arg) {
			console.log(arg)
		}
	})
}
// 以上都是放在請求體中
           

2)基于請求頭:

a. 在cookie中擷取csrftoken對應的值 l2kEqQLhR1gH0hh3ioZ1dfxT3iSwjXoKTf7GNFggJZ7E6DROB6k33L7vdqe5lV1v
b. 發送請求時,放在請求頭中也可以

function ajaxSubmit() {
	$.ajax({
		url: "/icbc.html",
		type: 'POST',
		data: {'k1':'v1','k2':'v2'},
		headers: {"X-CSRFToken": $.cookie('csrftoken')},
		success:function (arg) {
			console.log(arg)
		}
	})
}

           

跨站請求僞造對策

跨站請求僞造的危害非常之大,是以無論是使用者還是開發人員都應該引起足夠的重視,下面是給Web應用程式終端使用者和Web應用程式開發人員的一些有用的建議。

使用者:

因為CSRF漏洞有流行之趨勢,是以建議使用者遵循最佳實踐來降低風險,可以降低風險的習慣包括:

使用Web應用程式之後立即登出

不要讓浏覽器儲存使用者名/密碼,也不要讓站點“記住”您不要使用同一個浏覽器同時通路敏感的應用程式和随意沖浪;如果必須同時做多件事情的話,最好單獨使用不同的浏覽器。

對于支援HTM格式郵件/浏覽器內建式軟體以及內建了新聞閱讀程式/浏覽器的軟體都會帶來額外的風險,因為隻要檢視郵件或者新聞就有可能被迫執行一次攻擊,是以使用這類軟體時格外小心。

開發人員:

開發人員應當向URL添加跟會話有關資訊。該攻擊類型之是以得逞,是因為會話是由cookie唯一辨別的,并且該cookie是由浏覽器自動發送的。

如果我們在URL級别為會話生成其它相關資訊,那麼就會給攻擊者為發動攻擊而了解URL的結構造成更多的障礙。

至于其它的對策,雖然也無法解決該問題,但是能夠使得利用該漏洞更加困難,例如使用POST而不是GET。雖然POST請求可以通過JavaScript進行模仿,但是它提高了發動這種攻擊的難度。使用中間确認頁也能帶來相同的效果,比如“您确信要這樣做嗎?”之類的頁面。雖然攻擊者可以繞過這些措施,但是這些措施提供了實施攻擊的難度。是以,不能完全依賴這些手段來保護您的應用程式。自動登出機制也能減輕這種攻擊帶來的危害,但這最終依賴于具體情況(一個整天跟有這種漏洞的網絡銀行程式打交道的使用者所面臨的風險要遠遠大于臨時使用同一網絡銀行的使用者所面臨的風險)。

繼續閱讀