跨站请求伪造(csrf)
跨站请求伪造(csrf)
一、钓鱼网站
什么是xxx
为什么要有xxx
如何使用xxx
钓鱼网站:就类似于你搭建了一个跟银行一模一样的web页面, 用户在你的网站转账的时候输入用户名 密码 对方账户, 银行里面的钱确实少了 但是发现收款人变了
原理: 你写的form表单中 用户的用户名 密码都会真实的提交给银行后台, 但是收款人的账户却不是用户填的 你暴露给用户的是一个没有name属性的input框,你自己提前写好了一个隐藏的带有name和value的input框
解决钓鱼网站的策略: 只要是用户想要提交post请求的页面 我在返回给用户的时候就提前设置好一个随机字符串, 当用户提交post请求的时候 我会自动先取查找是否有该随机字符串 , 如果有 正常提交, 如果没有 直接报403
二、csrf
2.1 在form表单中实现
在form表单中添加: {% csrf_token %}
<!--login.html-->
<body>
<form action="" method="post">
{% csrf_token %}
<p>
用户: <input type="text" name="username">
</p>
<p>
密码: <input type="password" name="password">
</p>
<p>
<input type="submit" value="提交">
</p>
</form>
页面展示
对应的中间件:django.middleware.csrf.CsrfViewMiddleware
# 对应的中间件
'django.middleware.csrf.CsrfViewMiddleware',
2.2 AJAX请求实现
方式一:
<!--login.html-->
<body>
<form action="" method="post">
{% csrf_token %}
<!--通过js找到这个隐藏的框-->
</form>
<button id="b1">发ajax</button>
<script>
$('#b1').click(function () {
$.ajax({
url: "",
type: "post",
// 第一种方式
data: {
"username": "randy",
"password": 123,
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val() // 使用JQuery取出csrfmiddlewaretoken的值,拼接到data中
},
{#data:{'username':'randy','csrfmiddlewaretoken':'{{ csrf_token }}'},#}
{#data:{'username':'randy'},#}
success: function (data) {
alert(data)
}
})
})
</script>
方式二:
<button id="b1">发ajax</button>
{#<script src="/static/setup.js"></script>#}
<script>
$('#b1').click(function () {
$.ajax({
url:"",
type:"post",
// 第二种方式
data:{'username':'randy','csrfmiddlewaretoken':'{{ csrf_token }}'},
success:function (data) {
alert(data)
}
})
})
</script>
方式三(引入js):
# stupe.js
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;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
<!--login.html-->
<script src="/static/setup.js"></script>
<script>
$('#b1').click(function () {
$.ajax({
url:"",
type:"post",
// 第三种方式
data:{'username':'randy'},
success:function (data) {
alert(data)
}
})
})
</script>
三、装饰器实现(csrf)
<body>
<form action="crsf_cbv" method="post">
{# {% csrf_token %}#}
<p>
用户: <input type="text" name="username">
</p>
<p>
密码: <input type="password" name="password">
</p>
<p>
<input type="submit" value="提交">
</p>
</form>
</body>
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',
# 'app.mymiddleware.my_middleware.M1',
# 'app.mymiddleware.my_middleware.M2',
]
3.1 csrf_exempt不进行校验
# urls.py
from app import views
urlpatterns = [
url(r'^exem/', views.exem),
]
# views.py
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
@csrf_exempt
def exem(request):
"""前端不需要csrf字符串验证 {% csrf_token %} """
return HttpResponse("exempt不进行校验")
3.2 csrf_protect进行校验
# urls.py
from app import views
urlpatterns = [
url(r'^protect/', views.protect),
]
# views.py
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
@csrf_protect
def protect(request):
""""""前端需要 csrf 字符串验证{% csrf_token %} """"""
return HttpResponse("protect 校验")
3.3 给CBV添加csrf校验装饰器
<body>
<form action="crsf_cbv" method="post">
{# {% csrf_token %}#}
<p>
用户: <input type="text" name="username">
</p>
<p>
密码: <input type="password" name="password">
</p>
<p>
<input type="submit" value="提交">
</p>
</form>
</body>
不需要验证
# urls.py
urlpatterns = [
url(r'^crsf_cbv/', views.MyCrsfCbv.as_view()),
]
# views.py
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
from django.views import View
# post不验证不需要csrf字符串
class MyCrsfCbv(View):
def get(self, request):
return HttpResponse('hahaha')
def post(self, request):
return HttpResponse('post')
结果post
需要验证
方式一:
# urls.py
urlpatterns = [
url(r'^crsf_cbv/', views.MyCrsfCbv.as_view()),
]
# views.py
# 导入模块
# method_decorator(装饰器,方法名)
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
from django.views import View
# 第一种方式验证,改变装饰第一个参数进行验证或不验证
# cbv装csrf_exempt装饰器,只能给dispatch添加校验
# @method_decorator(csrf_exempt,name='dispatch')
# cbv装csrf_protect,指明道姓的给谁添加校验csrf,name=get/post
@method_decorator(csrf_protect, name='post')
class MyCrsfCbv(View):
def get(self, request):
return HttpResponse('hahaha')
@method_decorator(csrf_protect) # 有无
def post(self, request):
return HttpResponse('post')
方式二:
# urls.py
urlpatterns = [
url(r'^crsf_cbv/', views.MyCrsfCbv.as_view()),
]
# views.py
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
from django.views import View
class MyCrsfCbv(View):
# 第二种
# @method_decorator(csrf_exempt) # 不进行csrf校验
@method_decorator(csrf_protect) # 进行csrf校验
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, request):
return HttpResponse('hahaha')
def post(self, request):
return HttpResponse('post')
总结:
-
在给CBV装饰的时候有区别
-
需要导入一个固定的装饰器 method_decorator(装饰器,方法名)
-
csrf_exempt这个装饰器比较特殊 装饰的时候 只能给dispatch方法装
-
-
其他的装饰器 自定义的 模块的
- 直接类外面指名道姓的装
- 给dispatch装
- 直接装在方法上
在当下的阶段,必将由程序员来主导,甚至比以往更甚。