DAY85-Django框架(十五) 中间件和CSRF跨站伪装请求
Posted xvchengqi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DAY85-Django框架(十五) 中间件和CSRF跨站伪装请求相关的知识,希望对你有一定的参考价值。
一、中间件
1.定义
? 中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。
? 每次请求到视图之前,或者响应到浏览器之前都会经过中间件的筛选
2.基本使用
MIDDLEWARE = [
‘django.middleware.security.SecurityMiddleware‘,
‘django.contrib.sessions.middleware.SessionMiddleware‘,
‘django.middleware.common.CommonMiddleware‘,
‘django.middleware.csrf.CsrfViewMiddleware‘,
‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
‘django.contrib.messages.middleware.MessageMiddleware‘,
‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
]
#这是Django内部已经封装好的中间件
3.自定义中间件
#中间件的方法
process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
#第一步:创建一个PY文件
#第二步:导入from django.utils.deprecation import MiddlewareMixin
from django.utils.deprecation import MiddlewareMixin
#第三步:新建自定义中间件process_request和process_response是必须的
class Mymiddle1(MiddlewareMixin):
def process_request(self, request):
print(‘Mymiddle1 request‘)
def process_response(self, request, response):
print(‘Mymiddle1 response‘)
return response#process_response一定要返回HttpResponse对象
class Mymiddle2(MiddlewareMixin):
def process_request(self, request):
print(‘Mymiddle2 request‘)
def process_response(self, request, response):
print(‘Mymiddle2 response‘)
return response
#第四步:写一个视图函数
from django.shortcuts import render,HttpResponse
def index(requset):
print(‘index‘)
return HttpResponse(‘ok‘)
#第五步:在setting里设置
MIDDLEWARE = [
‘django.middleware.security.SecurityMiddleware‘,
‘django.contrib.sessions.middleware.SessionMiddleware‘,
‘django.middleware.common.CommonMiddleware‘,
‘django.middleware.csrf.CsrfViewMiddleware‘,
‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
‘django.contrib.messages.middleware.MessageMiddleware‘,
‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
‘app01.mymiddleware.Mymiddle1‘,
‘app01.mymiddleware.Mymiddle2‘
]
#请求是从上而下一层层的通过中间件的,之后才是执行视图层,而响应是从下而上一层层的返回去。
#结果
Mymiddle1 request
Mymiddle2 request
index
Mymiddle2 response
Mymiddle1 response
结论
? 如果请求再通过中间件时,在process_request遇到了return返回,那么这个请求就不会再往下走进入视图了,而是从当前的process_response往上的返回
from django.shortcuts import render,HttpResponse
class Mymiddle1(MiddlewareMixin):
def process_request(self, request):
print(‘Mymiddle1 request‘)
print(‘Mymiddle1 请求中断‘)
return HttpResponse(‘Mymiddle1 请求中断‘)
def process_response(self, request, response):
print(‘Mymiddle1 response‘)
return response#process_response一定要返回HttpResponse对象
#结果
Mymiddle1 request
Mymiddle1 请求中断
Mymiddle1 response
4.process_view
#语法
process_view(self,
request, #request是HttpRequest对象。
callback, #view_func是Django即将使用的视图函数。
callback_args, #view_args是将传递给视图的位置参数的列表.
callback_kwargs #view_kwargs是将传递给视图的关键字参数的字典。
)
? 它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,HttpResponse
class Mymiddle1(MiddlewareMixin):
def process_request(self, request):
print(‘Mymiddle1 request‘)
def process_response(self, request, response):
print(‘Mymiddle1 response‘)
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("Mymiddle1 view")
class Mymiddle2(MiddlewareMixin):
def process_request(self, request):
print(‘Mymiddle2 request‘)
def process_response(self, request, response):
print(‘Mymiddle2 response‘)
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("Mymiddle2 view")
Mymiddle1 request
Mymiddle2 request
Mymiddle1 view
Mymiddle2 view
index
Mymiddle2 response
Mymiddle1 response
结论
如果中间件1中process_view在return,那么就会跳过之后的process_view和视图函数,直接执行process_response
def process_view(self, request, callback, callback_args, callback_kwargs):
print(‘Mymiddle1 view中断‘)
return HttpResponse(‘Mymiddle1 view中断‘)
#结果
Mymiddle1 request
Mymiddle2 request
Mymiddle1 view中断
Mymiddle2 response
Mymiddle1 response
可以在process_view中调用视图函数,也会跳过之后的process_view和视图函数,直接执行process_response
def process_view(self, request, callback, callback_args, callback_kwargs):
res = callback(request)
print(‘Mymiddle1 view中断‘)
return res
#结果
Mymiddle1 request
Mymiddle2 request
index
Mymiddle1 view中断
Mymiddle2 response
Mymiddle1 response
5.process_exception
process_exception(self,
request, #HttpRequest对象
exception #视图函数异常产生的Exception对象
)
? 这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,HttpResponse
class Mymiddle1(MiddlewareMixin):
def process_request(self, request):
print(‘Mymiddle1 request‘)
# print(‘Mymiddle1 请求中断‘)
# return HttpResponse(‘Mymiddle1 请求中断‘)
def process_response(self, request, response):
print(‘Mymiddle1 response‘)
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("Mymiddle1 view")
# res = callback(request)
# print(‘Mymiddle1 view中断‘)
# return res
def process_exception(self,request,exception):
print("Mymiddle1 exception")
class Mymiddle2(MiddlewareMixin):
def process_request(self, request):
print(‘Mymiddle2 request‘)
def process_response(self, request, response):
print(‘Mymiddle2 response‘)
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("Mymiddle2 view")
def process_exception(self,request,exception):
print("Mymiddle2 exception")
#结果
#当视图函数没有出现异常时,触发process_exception方法
Mymiddle1 request
Mymiddle2 request
Mymiddle1 view
Mymiddle2 view
index
Mymiddle2 response
Mymiddle1 response
结论
process_exception是在视图函数执行完毕之后出发的,和process_response一样是从下往上的执行
如果此时视图函数出现了异常
def index(requset):
print(‘index‘)
asd#未定义的变量
return HttpResponse(‘ok‘)
1.自定义的process_exception中没有return,那么会内置的来抛出异常
Mymiddle1 request
Mymiddle2 request
Mymiddle1 view
Mymiddle2 view
index
Mymiddle2 exception
Mymiddle1 exception
...
异常信息
...
Mymiddle2 response
Mymiddle1 response
2.自定义的process_exception中写了return,那么就不会在执行之后的process_exception,而是执行process_response
def process_exception(self,request,exception):
print(‘Mymiddle2 抛出异常‘)
return HttpResponse(‘Mymiddle2 抛出异常‘)
#结果
Mymiddle1 request
Mymiddle2 request
Mymiddle1 view
Mymiddle2 view
index
Mymiddle2 抛出异常
Mymiddle2 response
Mymiddle1 response
? 如果Mymiddle2的 process_exception没有返回HttpResponse对象,那么就会由从下往上的顺序,去找有没有返回HttpResponse对象的process_exception,直到找到。
#结果
Mymiddle1 request
Mymiddle2 request
Mymiddle1 view
Mymiddle2 view
index
Mymiddle2 exception
Mymiddle1 抛出异常
Mymiddle2 response
Mymiddle1 response
二、CSRF跨站请求伪造
1.CSRF
? CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性
可以这样来理解:
? *攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的*,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。
2.攻击原理
要完成一次CSRF攻击,受害者必须依次完成两个步骤:
1.登录受信任网站A,并在本地生成Cookie。
2.在不登出A的情况下,访问危险网站B。
看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:
1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)
3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。
3.防御攻击
? 目前防御 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求中添加 token 并验证;在 HTTP 头中自定义属性并验证
? 对于CSRF的防御,Django内部做了一个中间件来处理CSRF的攻击
MIDDLEWARE = [
‘django.middleware.csrf.CsrfViewMiddleware‘,
]
? 它设置了一个随机字符串,通过响应发给受信任的用户,当受信任用户发送请求时必须携带一模一样的随机字符串,才可以进入服务器。而且每次响应服务器给浏览器都不一样的,有效的做到了对CSRF的防御
方式一:请求中添加 token 并验证
? 放在表单中请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<!--渲染之后,获得随机字符串
<input type="hidden" name="csrfmiddlewaretoken" value="RaxCabhuZdpvWxQ67whrdmImNPYuFVQ5Ies9X52TVrIbt1AdVfxPNbMUFVKgLWp4">
-->
<input type="text" name="name" placeholder="用户名">
<input type="text" name="pwd" placeholder="密码">
<input type="submit">
</form>
</body>
</html>
from django.shortcuts import render, HttpResponse
def csrf_test(request):
if request.method == ‘GET‘:
return render(request, ‘csrf_test.html‘)
else:
name = request.POST.get(‘name‘)
pwd = request.POST.get(‘pwd‘)
token = request.POST.get(‘csrfmiddlewaretoken‘)
print(token)
if name == ‘xcq‘ and pwd == ‘123‘:
msg = ‘登陆成功‘
else:
msg = ‘登录失败‘
return HttpResponse(msg)
? 通过AJAX
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
<script src="{% static ‘jquery-3.3.1.js‘ %}"></script>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<input type="text" name="name" placeholder="用户名">
<input type="text" name="pwd" placeholder="密码">
</form>
<button id="btn">登录</button>
</body>
<script>
//post请求
$(‘#btn‘).click(function () {
$.ajax({
url:‘/csrf_test/‘,
type:‘post‘,
data:{‘csrfmiddlewaretoken‘:$(‘[name="csrfmiddlewaretoken"]‘).val()},
//或者 data:{‘csrfmiddlewaretoken‘:‘{{ csrf_token }}‘},
success:function (data) {
alert(data)
}
})
})
//get请求
$(‘#btn‘).click(function () {
$.ajax({
url:‘/csrf_test/?id={{ csrf_token }}‘ ,
type:‘get‘,
success:function (data) {
alert(data)
}
})
})
</script>
</html>
方式二:在 HTTP 头中自定义属性并验证
//获取cookie里的csrftoken
$("#btn").click(function () {
var token=$.cookie(‘csrftoken‘)
$.ajax({
url: ‘/csrf_test/‘,
headers:{‘X-CSRFToken‘:token},
type: ‘post‘,
data: {
‘name‘: $(‘[name="name"]‘).val(),
‘password‘: $("#pwd").val(),
},
success: function (data) {
alert(data)
}
})
})
局部禁用和局部使用
FBV模式
from django.views.decorators.csrf import csrf_exempt,csrf_protect
#局部禁用,全局得使用
@csrf_exempt
def csrf_disable(request):
print(request.POST)
return HttpResponse(‘ok‘)
#局部使用,全局得禁用
@csrf_protect
def csrf_disable(request):
print(request.POST)
return HttpResponse(‘ok‘)
CBV模式
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@method_decorator(csrf_protect,name=‘dispatch‘)
#CBV的csrf装饰器,只能加载类上且指定方法为dispatch,或dispatch方法上
class Csrf_disable(View):
# @method_decorator(csrf_protect)
def dispatch(self, request, *args, **kwargs):
ret=super().dispatch(request, *args, **kwargs)
return ret
def get(self,request):
return HttpResponse(‘ok‘)
def post(self,request):
return HttpResponse(‘post---ok‘)
以上是关于DAY85-Django框架(十五) 中间件和CSRF跨站伪装请求的主要内容,如果未能解决你的问题,请参考以下文章