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

相关推荐
沙漠之主5 小时前
C++编程教学设计资料:从入门到实战的完整课程方案
java·前端·c++
hasty5 小时前
不上传新包,也能改变用户拿到的版本:npm dist-tag 的 OIDC 权限治理
前端·npm·node.js
FPGA小徐5 小时前
PA1 基础设施(2)深度解析:NEMU 中 ITRACE、MTRACE、FTRACE、IRINGBUF、ETRACE、DIFFTEST 全解析
java·服务器·网络
Csvn5 小时前
diff 算法(虚拟 DOM Reconciliation)
前端
前端snow7 小时前
ai agent --- 文件存储
前端
Frag0ut7 小时前
Chrome四大版本获取及共存指南:Stable/Beta/Dev/Canary
前端·chrome·浏览器·dev·beta·canary·共存版
IT_陈寒8 小时前
SpringBoot自动配置坑了我一把,原来是这样绕过去的
前端·人工智能·后端
广州华水科技8 小时前
单北斗GNSS变形监测在大坝安全监测中的应用与优势
前端
zeng不错8 小时前
B站 173 个投稿活动看到眼瞎?我写了个扩展,主打一个精准打击
前端
Latchh8 小时前
前端导出的JPEG红字发糊,质量要拉到100才清楚
前端·图像处理·人工智能·计算机视觉