第十篇:跨站请求伪造csrf

假如想象 提交于 2020-02-07 21:04:45

钓鱼网站

钓鱼网站和正规网站的页面一模一样,提交网页数据的url也一样,但是会在页面中设置隐藏属性的form表单。例如转账:给用户书写的form表单,对方账号的input没有name属性,然后另外写一个具有默认的并且是隐藏的具有name属性的input框。

form表单如何通过csrf校验

为了防止此类事情的发生,我们使用csrf_token生成随机字符串

在form表单内添加:

{% csrf_token %}

browser客户端向服务端发动get请求,服务端返回给browser一串随机的字符串,当browser向服务端发送post请求时,会携带上该字符串,服务端会先对该随机字符串进行校验,如果客户端携带的字符串和服务器上的字符串一致,服务端会允许客户端提交post请求,否则会被forbidden掉。

当客户端向django服务端发送post请求,django中间件django.middleware.csrf.CsrfViewMiddleware会获取post中携带的name为“csrfmiddlewaretoken”的value是否和之前返回给客户端的value一致。

ajax如何通过csrf校验

第一种:自己手动获取

<body>
<form action="" method="post">
    {% csrf_token %}
    <p>用户名:<input type="text" name="username"></p>
    <p>密码: <input type="text" name="password"></p>
    <p><button id="d1">发送ajax请求</button></p>
</form>
 
<script>
    $('#d1').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            data: {'username': 'jason', 'csrfmiddlewaretoken': $('input[name="csrfmiddlewaretoken"]').val()},
            success: function (data) {
                alert(data)
            }
        })
 
    })
</script>
</body>
</html>

第二种:利用模板语法,获取到随机字符串

<body>
<form action="" method="post">
    {% csrf_token %}
    <p>用户名:<input type="text" name="username"></p>
    <p>密码: <input type="text" name="password"></p>
    <p><button id="d1">发送ajax请求</button></p>
</form>
 
<script>
    $('#d1').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            data: {'username': 'jason', 'csrfmiddlewaretoken': '{{ csrf_token }}'},
            success: function (data) {
                alert(data)
            }
        })
 
    })
</script>
</body>
</html>

第三种:通用方式,引用外部js文件,此种方法是django官网推荐的

在static文件夹下新建一个js文件(eg:myset.py)

// static/myset.py
 
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);
    }
  }
});

使用方法:引入csrf的js文件

<body>
<form action="" method="post">
    {% csrf_token %}
    <p>用户名:<input type="text" name="username"></p>
    <p>密码: <input type="text" name="password"></p>
    <p><button id="d1">发送ajax请求</button></p>
</form>
 
<!--此处引入csrf文件-->
{% load static %}
<script src="{% static 'myset.js' %}"></script>
<script>
    $('#d1').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            data: {'username': 'jason''},
            success: function (data) {
                alert(data)
            }
        })
 
    })
</script>
</body>
</html>

# csrf相关装饰器

可以局部对csrf的校验进行更改

针对FBV

针对FBV的视图方法:

首先需要导入包:

# views.py
 
from django.views.decorators.csrf import csrf_exempt, csrf_protect

局部不校验csrf

当我们网站整体都检验csrf的时候,我们可以让某几个视图函数不校验,需要使用csrf_exempt模块:

# views.py
 
@csrf_exempt  # 不校验被装饰的函数
def func(request):
    pass

局部校验csrf

当我们网站整体都不校验csrf的时候,我们可以让某几个视图函数校验,需要使用csrf_protect模块:

# views.py
 
@csrf_protect  # 校验被装饰函数
def func(request):
    pass

针对CBV

针对CBV的视图方法:

针对下面的CBV的方法,我们如何做到局部修改csrf校验呢?

# views.py
 
from django.views import View
class MyHome(View):
    def get(self, request):
        return HttpResponse('get')
 
    def post(self, request):
        return HttpResponse('post')

局部校验csrf

全局不校验,单单针对局部的CBV的方法进行校验:

第一种方法:直接装饰在CBV内部的函数之上

# views.py
 
# 需要导入模块
from django.views.decorators.csrf import csrf_protect
from django.utils.decorators import method_decorator
 
class MyHome(View):
 
    def get(self, request):
        return HttpResponse('get')
 
    @method_decorator(csrf_protect)  # 使CBV中的函数被校验csrf
    def post(self, request):
        return HttpResponse('post')

第二种方法:装饰在CBV类上,指定装饰CBV类中的哪个函数

# views.py
 
# 需要导入模块
from django.views.decorators.csrf import csrf_protect
from django.utils.decorators import method_decorator
 
@method_decorator(csrf_protect, name='post')  # 指名道姓的给CBV中的函数装饰
class MyHome(View):
    def get(self, request):
        return HttpResponse('get')
 
    def post(self, request):
        return HttpResponse('post')

局部不校验csrf

全局校验,单单针对局部的CBV中的方法不校验:

第一种方法:直接装饰在CBV内部的函数之上

# views.py
 
# 需要导入模块
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
 
class MyHome(View):
 
    def get(self, request):
        return HttpResponse('get')
 
    @method_decorator(csrf_exempt)  # 使CBV中函数不被校验csrf
    def post(self, request):
        return HttpResponse('post')

第二种方式:装饰在CBV类上,指定装饰CBV类中的哪个函数

# views.py
 
# 需要导入模块
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
 
@method_decorator(csrf_exempt, name='post')  # 指名道姓的给CBV中的函数装饰
class MyHome(View):
    def get(self, request):
        return HttpResponse('get')
 
    def post(self, request):
        return HttpResponse('post')

给CBV中的所有函数加装饰器

我们之前看源码,发现在匹配CBV中类的方法的时候,统一由dispatch函数分发,因此我们可以重写父类中的dispatch方法,给dispatch方法加装饰器,再return出去。

这种方式,相当于对CBV中的所有函数都装饰上了,影响的是CBV中的所有函数。

from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
 
 
# @method_decorator(csrf_exempt, name='post')  # csrf_exempt 第二种方式不行
@method_decorator(csrf_exempt, name='dispatch')  # 可以!!!
class MyHome(View):  # APIView
    # @method_decorator(csrf_protect)  # 第三种 类中所有的方法都装
    # @method_decorator(csrf_exempt)  # csrf_exempt 第三种方式可以
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request,*args,**kwargs)
 
    def get(self,request):
        return HttpResponse('get')
    # @method_decorator(csrf_protect)  # 第一种方式
    # @method_decorator(csrf_exempt)  # csrf_exempt 第一种方式不行
    def post(self,request):
        return HttpResponse('post')

特例:对于CBV来说,当我们打开全局校验csrf的时候,针对CBV中的某个方法不进行校验的时候,这个csrf_exempt只能给dispatch方法装饰(在CBV中想要使用局部不校验,只能装饰在重写父类的dispatch函数上)。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!