CSRF***的目標,通常都會產生「寫數據」操做的URL,好比「增」、「刪」、「改」;而「讀數據」操做並非CSRF***的目標,由於在CSRF的***過程當中***者沒法獲取到服務器端返回的數據,***者只是借用戶之手觸發服務器動做,因此讀數據對於CSRF來講並沒有直接的意義(可是若是同時存在XSS漏洞或者其餘的跨域漏洞,則可能會引發別的問題,在這裏,僅僅就CSRF對抗自己進行討論)。
所以,在Web應用開發中,有必要對「讀操做」和「寫操做」予以區分,好比要求全部的「寫操做」都使用HTTP POST。
在不少講述CSRF防護的文章中,都要求使用HTTP POST進行防護,但實際上POST自己並不足以對抗CSRF,由於POST也是能夠自動提交的。可是POST的使用,對於保護token有着積極的意義,而security token的私密性(不可預測性原則),是防護CSRF***的基礎。
對於Web框架來講,能夠自動地在全部涉及POST的代碼中添加token,這些地方包括全部的form表單、全部的Ajax POST請求等。
完整的CSRF防護方案,對於Web框架來講有如下幾處地方須要改動。
(1)在Session中綁定token。若是不能保存到服務器端Session中,則能夠替代爲保存到Cookie裏。
(2)在form表單中自動填入token字段,好比 <input type=hidden name="anti_csrf_token" value="$token" />。
(3)在Ajax請求中自動添加token,這可能須要已有的Ajax封裝實現的支持。
(4)在服務器端對比POST提交參數的token與Session中綁定的token是否一致,以驗證CSRF***。
在Rails 中,要作到這一切很是簡單,只須要在Application Controller中增長一行便可:
protect_from_forgery :secret => "123456789012345678901234567890..."
它將根據secret和服務器端的隨機因子自動生成token,並自動添加到全部form和由Rails生成的Ajax請求中。經過框架實現的這一功能大大簡化了程序員的開發工做。
在Django中也有相似的功能,可是配置稍微要複雜點。
首先,將 django.middleware.csrf.CsrfViewMiddleware 添加到 MIDDLEWARE_CLASSES中。
('django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',)
而後,在form表單的模板中添加token。
<form action="." method="post">{% csrf_token %}
接下來,確認在View層的函數中使用了django.core.context_processors.csrf,若是使用的是 RequestContext,則默認已經使用了,不然須要手動添加。
from django.core.context_processors import csrf
from django.shortcuts import render_to_response
def my_view(request):
c = {}
c.update(csrf(request))
# ... view code here
return render_to_response("a_template.html", c)
這樣就配置成功了,能夠享受CSRF防護的效果了。
在 Ajax請求中,通常是插入一個包含了token的HTTP頭,使用HTTP頭是爲了防止token泄密,由於通常的JavaScript沒法獲取到HTTP頭的信息,可是在存在一些跨域漏洞時可能會出現例外。
下面是一個在Ajax中添加自定義token的例子。
$(document).ajaxSend(function(event, xhr, settings) {
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function sameOrigin(url) {
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
function safeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
});
在Spring MVC以及一些其餘的流行Web框架中,並無直接提供針對CSRF的保護,所以這些功能須要本身實現。
本文節選自《白帽子講Web安全》一書。
圖書詳細信息:http://bvbroadview.blog.51cto.com/addblog.php