CSRF跨站请求伪造。也被称为One Click Attack和Session Riding,通常缩写为CSRF或XSRF。如果从名字你还不知道它表示什么,你可以这样理解:攻击者(黑客,钓鱼网站)盗用了你的身份,以你的名义发送恶意请求,这些请求包括发送邮件,发送消息,盗取账号,购买商品,银行转账,从而使你的个人隐私泄露和财产损失。1.我搭建一个跟正规网站一模一样的界面(中国银行)2.用户不小心进入到了我们的网站,用户给某个人打钱3.打钱的操作确确实实是提交给了中国银行的系统,用户的钱也确确实实减少了4.但是唯一不同的是打钱的账户不是用户想要打的账户变成了一个莫名其妙的账户
1.我们在钓鱼网站的页面,针对对方账户,只给用户提供一个没有name属性的普通input框2.然后我们在内部隐藏一个已经写好name和value和input框
正规银行# transfer.html<h1>我是正儿八经的网站</h1><form action="" method="post"> <p>username: <input type="text" name="username"></p> <p>target_user: <input type="text" name="target_user"></p> <p>money: <input type="text" name="money"></p> <input type="submit"></form># viewsdef transfer(request): if request.method == 'POST' username = request.POST.:get('username') target_user = request.POST.get('target_user') money = request.POST.get('money') print('%s给%s转了%s元' % (username, target_user, money)) return render(request, 'transfer.html')不正规钓鱼网站# transfer.html<h1>我是钓鱼网站</h1><form action="http://127.0.0.1:8000/transfer/" method="post"> <p>username: <input type="text" name="username"></p> <p>target_user: <input type="text"></p> {# 隐藏 #} <input type="text" name="target_user" value="jason" > <p>money: <input type="text" name="money"></p> <input type="submit"></form> def transfer(request): return render(request, 'transfer.html') 
看到这里,你也许会问:“如果我不满足以上两个条件中的一个,我就不会受到CSRF攻击”。是滴,确实如此,但是你不能保证以下情况不会发生:
# csrf跨站请求伪造效验1.网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识。2.当这个页面朝后端发送post请求的时候,我的后端会先效验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden),如果成功则正常执行。# 1.打开settings内中间件打开<form action="" method="post"> {% csrf_token %} // 给正规的网站页面都添加一个唯一标识 <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text" name="target_user"></p> <p>money:<input type="text" name="money"></p> <input type="submit"></form>
// name对应的值 拿到标签对应的值 data:{"username":'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},data:{"username":'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},data:{"username":'jason'},STATIC_URL = '/static/'STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static')]transfer.html
<button id="d1">ajax请求</button>{# 动态解析 静态文件#}{% load static %}<script src="{% static 'js/mysetup.js' %}"></script><script> $('#d1').click(function () { $.ajax({ url:'', // 不写默认提交地址当前 type:'post', // 请求 // 第一种 {#data:{"username":"jason", 'csrfmiddlewaretoken':$('[name=csrfmiddlewretoken]').val()},#} // 第二种 {#data:{"username":'jason', 'csrfmiddlewaretoken':'{{ csrf_token }}'},#} data:{"username":'jason'}, // 数据 success:function () { } }) })</script>
不论是ajax还是谁,只要是向我Django提交post请求的数据,都必须校验csrf_token来防伪跨站请求将下面的文件配置到你的Django项目的静态文件中,在html页面上通过导入该文件即可自动帮我们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件之前,需要先导入jQuery,因为这个配置文件内的内容是基于jQuery来实现的)
更多细节详见:Djagno官方文档中关于CSRF的内容
getCookie方法:
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');使用$.ajaxSetup()方法为ajax请求统一设置
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); } }});1.网站整体都不校验csrf/配置文件中间件注释掉,就单单几个视图函数需要校验2.网站整体都校验csrf/配置文件中间件不注释,就单单几个视图函数不校验from django.views.decorators.csrf import csrf_protect,csrf_exemptcsrf_protect 需要校验 针对csrf_protect符合我们之前所学的装饰器的三种玩法 csrf_exempt 忽视校验 针对csrf_exempt只能给dispatch方法加才有效1.注释掉给前端页面form表单添加的唯一标识{% csrf_token %}
# @csrf_protectdef transfer(request): if request.method == 'POST': username = request.POST.get('username') target_user = request.POST.get('target_user') money = request.POST.get('money') print('%s给%s转了%s元'%(username,target_user,money)) return render(request,'transfer.html')###### 5.整个网站都效验csfr,就单单一个视图函数不校验* settings设置第四个中间件打开```python# @csrf_exemptdef transfer(request): if request.method == 'POST': username = request.POST.get('username') target_user = request.POST.get('target_user') money = request.POST.get('money') print('%s给%s转了%s元'%(username,target_user,money)) return render(request,'transfer.html')
from django.utils.decorators import method_decoratorfrom django.views import View# @method_decorator(csrf_protect,name='post') # 针对csrf_protect 第二种方式全局,指名道姓,可以# @method_decorator(csrf_exempt,name='post') # 针对csrf_exempt 第二种方式不可以@method_decorator(csrf_exempt,name='dispatch')class MyCsrfToken(View): # @method_decorator(csrf_protect) # 针对csrf_protect 第三种方式可以 # @method_decorator(csrf_exempt) # 针对csrf_exempt 第三种方式可以 def dispatch(self, request, *args, **kwargs): return super(MyCsrfToken, self).dispatch(request,*args,**kwargs) def get(self,request): return HttpResponse('get') # @method_decorator(csrf_protect) # 针对csrf_protect 第一种方式可以 # @method_decorator(csrf_exempt) # 针对csrf_exempt 第一种方式不可以 def post(self,request): return HttpResponse('post')1.针对csrf_exempt只能加在dispatch才能生效2.以下两种是等价的# 1.指名道姓给dispatch加csrf_exempt@method_decorator(csrf_exempt,name='dispatch')# 2.直接给dispatch加csrf_exempt装饰器@method_decorator(csrf_exempt) # 针对csrf_exempt 第三种方式可以 def dispatch(self, request, *args, **kwargs): return super(MyCsrfToken, self).dispatch(request,*args,**kwargs)