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

相关推荐
90后的晨仔6 小时前
从 H5 到 uni-app:一篇写给前端小白的"翻译指南"
前端·vue.js·前端框架
陈随易7 小时前
moon,apt和yum之外linux系统命令安装新选择
前端·后端·程序员
IT小盘7 小时前
13-企业Prompt模板-角色任务约束与输出格式
java·前端·prompt
徐小夕8 小时前
开源!我用SQLite + DuckDB打造了一款可视化AI问数平台
前端·算法·github
leslie1188 小时前
babel笔记
前端
用户059540174469 小时前
Redis 记忆存储踩坑实录:一个并发写入 Bug 让我排查了 4 小时
前端·css
黑桃小柒79 小时前
小,最终显示为TCP WINDOW FULL,TCP ZeroWindow。 仔细分析了下LWIP源码,还以为是内存管理出了问题,跟 ...
网络·网络协议·tcp/ip
小徐_23339 小时前
Wot UI 2.3.0 发布:二维码组件来了,Open Wot 与 wot-starter 同步更新
前端·微信小程序·uni-app
kyriewen10 小时前
Claude自己跑出去hack了3家公司——我为什么还在用它写代码
前端·ai编程·claude
IT_陈寒10 小时前
SpringBoot自动配置的坑,这次真踩疼我了
前端·人工智能·后端