Web 框架与 CSRF 防御

关于 CSRF 的攻击原理和防御方案,在本书“跨站点请求伪造”一章中有所阐述。在 Web 框架中可以使用 security token 解决 CSRF 攻击的问题。

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 字段,比如 。

(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 的保护,因 此这些功能需要自己实现。

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值