Django DeleteView without confirmation template, but with CSRF attack
随便写点东西吧。
Django的Class based view十分好用,也很灵活。其中DeleteView( https://docs.djangoproject.com/en/1.10/ref/class-based-views/generic-editing/#django.views.generic.edit.DeleteView )有点特别蛋疼的,他理想的流程是这样:点击删除 -> 跳转到确认页面 -> 点击确认删除 -> 删除对象 -> 跳转到success_url。
这样导致删除的流程特别麻烦,特别是这个“确认页面”:
因为“删除”是一个敏感操作,所以一定要有CSRF防御,所以点击上图这个“是的,我确认”按钮以后,会发送一个POST请求到后端的。Django会自动检查CSRF TOKEN。但实际上,我们也可以操作JavaScript向后端发送POST请求,而无需确认页面里的这个表单。
所以,我理想中的删除逻辑应该是这样的:点击删除 -> Javascript弹出确认框 -> 用户点击确认 -> Javascript生成一个表单 -> 提交 -> 跳转到success_url。
我写了个JavaScript函数,专门用来执行删除等需要POST的逻辑:
function submit(action_url) {
if(!confirm('确认要执行这个操作?')) {
return false;
}
var f = $('<form method="post"></form>');
var xsrf = $("{% csrf_token %}");
f.append(xsrf);
f.prop('action', action_url);
f.submit();
}
使用的时候,只要传入后端url作为action_url即可。
所以,我这个逻辑里面是不需要GET请求的,也就是说不需要写“确认删除”的模板,所以我上网上搜了一下如何才能不要这个模板: http://stackoverflow.com/questions/17475324/django-deleteview-without-confirmation-template ,果然是有人问过的。所以其实有很多人和我遇到同样的问题。
但我一看回答……:
很无语,直接把get导向post了,这样做必然会存在CSRF。因为 django.middleware.csrf.CsrfViewMiddleware 是不检查GET方法的:
准确来说,是不检查GET、HEAD、OPTIONS、TRACE方法。所以说,如果按照stackoverflow上面的事例来写代码的话,就是存在漏洞的。
另外,关于检查的方法。让我想到flask,flask-wtf有个小缺陷,默认情况下,它只检查POST/PUT/PATCH三个方法 https://github.com/lepture/flask-wtf/blob/f306c360f74362be3aac89c43cdc7c37008764fb/flask_wtf/csrf.py#L151 ,不知道开发者怎么想的。正常开发中,ajax请求里会存在很多DELETE方法,所以DELETE一定要检查CSRF TOKEN,否则很容易出现漏洞。
那么回到Django。既然上述做法会引发CSRF漏洞,那么我们怎么办?
我们分析一下问题,现在问题是:我们只需要POST方法,但默认的DeleteView要求提供GET和POST两个方法,并且GET方法需要一个模板,也就是“确认删除”这个页面的模板。如果我们不提供模板,用户在以GET方法访问这个页面的时候就会报错,但要写个模板,感觉多此一举,很不优雅。
(当然你可以无视报错,反正不影响正常使用,但作为一个强迫症我忍不了)
我们先分析一下Class based view的原理。Django的一个基类View类,其中有一个dispatch方法,所有的请求经由dispatch方法,再根据请求的方法具体分发到get、post、delete这样的函数里。
那么,一个请求允许哪些方法,是在_allowed_methods函数里定义的:
这个函数的意思就是:根据子类中定义过的方法名确定允许哪些方法。
比如,Django提供的BaseUpdateView类中定义了两个方法get和post:
所以,继承这个类的View一定允许GET和POST两种请求。而Python是个多继承语言,当它还继承了其他辅助类(Mixin),还可能会允许其他请求。那么,一旦用户的请求不在允许的范围内,就会调用http_method_not_allowed,具体现象就是返回405错误:
回到上面的问题,所以现在解决问题的方法就很明显了:重写get函数,让“GET”请求返回self.http_method_not_allowed()
就可以了:
class LinkDeleteView(AdminPermissionMixin, DeleteView):
model = Link
success_url = reverse_lazy('management-link-list')
permission_required = 'archives.delete_link'
def get(self, request, *args, **kwargs):
return self.http_method_not_allowed(request, *args, **kwargs)
就是这么简单。
或者,换个更调皮、更简略的写法:
class LinkDeleteView(AdminPermissionMixin, DeleteView):
model = Link
success_url = reverse_lazy('management-link-list')
permission_required = 'archives.delete_link'
get = DeleteView.http_method_not_allowed
想想为什么可以这么写,不懂的话可以看看Django源码,答案就在里面。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 转录组分析 | 使用Trimmomatic过滤Fastq文件
- 转录组分析 | 使用FastQC进行数据质控
- R语言绘图 | 给箱线图加个点
- R语言绘图 | 使用pheatmap快速绘制热图
- 可能是目前最详细从零开始配置 TypeScript 项目的教程
- 比对得到的SAM文件怎么看?
- ANTNet|端侧架构,精度速度双超MobileNetV2
- 9个动图带你进入PyQtGraph的强大可视化世界
- 转录组分析 | 使用STAR进行比对
- 使用R/qtl进行QTL分析
- 转录组分析 | 使用RSEM进行转录本定量
- 使用R语言计算遗传力
- 专属于六倍体小麦的Bioconductor注释包
- 轻松上传超过100M的文件至GitHub
- 转录组分析 | 使用DESeq2进行基因差异表达分析