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

相关推荐
郑板桥303 分钟前
tua-body-scroll-lock踩坑记录
前端·javascript
千鼎数字孪生-可视化23 分钟前
WebSocket实时推送技术:PLC状态监控大屏的高效实现路径
网络·websocket·网络协议
李白你好23 分钟前
这是一个用于扫描Zabbix系统中常见漏洞的工具,提供了图形界面(GUI)和命令行(CLI)两种使用方式。
网络·安全·zabbix
蒋星熠31 分钟前
破壁者指南:内网穿透技术的深度解构与实战方法
网络·数据库·redis·python·websocket·网络协议·udp
慢半拍iii1 小时前
JAVA Web —— A / 网页开发基础
前端
gnip1 小时前
pnpm 的 monorepo架构多包管理
前端·javascript
新手村领路人3 小时前
Firefox自定义备忘
前端·firefox
乖女子@@@3 小时前
css3新增-网格Grid布局
前端·css·css3
伐尘4 小时前
【CE】图形化CE游戏教程通关手册
前端·chrome·游戏·逆向
不想吃饭e4 小时前
在uniapp/vue项目中全局挂载component
前端·vue.js·uni-app