Django csrf跨站请求伪造,校验,CBV忽略与允许csrf校验


csrf跨站请求伪造

钓鱼网站,搭建一个网站与正规网站一模一样的界面,用户进入到我们的网站中,给指定用户转账,汇款确实提交到银行,但是收款人确实我们自己定义的人。

本质在钓鱼网站的页面针对对方账户,只给用户提供一个没有name属性的普通input框,在内部则隐藏一个已经写好的name和value的input框

代码举例:

转账用户:

被转账用户:

金额:

后端:

def transfer(request):
    if request.method == 'POST':
        print(f'{request.POST.get("name")}向{request.POST.get("user")}转战{request.POST.get("money")}')
        print(request.POST)
    return  render(request,'transfer.html')

前端用户输入被转账用户名称无name属性,使用自定义,所以前端不管给谁转账都是给junjie转账

如何规避上述问题?

csrf跨站请求伪造校验

网站再给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识,当这个页面朝后端发送post请求的时候,后端先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden),成功则正常执行

针对form表单{% csrf_token %}

{% csrf_token %}

username:

target_user:

money:

针对ajax设置固定的静态文件

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);
    }
  }
});

将下面的文件配置到你的Django项目的静态文件中,在html页面上通过导入该文件即可自动帮我们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件之前,需要先导入jQuery,因为这个配置文件内的内容是基于jQuery来实现的)

更多细节详见:Djagno官方文档中关于CSRF的内容

# ajax如何符合校验
// 第一种 利用标签查找获取页面上的随机字符串
{#data:{"username":'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},#}
// 第二种 利用模版语法提供的快捷书写
{#data:{"username":'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},#}
// 第三种 通用方式直接拷贝js代码并应用到自己的html页面上即可
data:{"username":'jason'}

csrf相关装饰器

现在提出连个需求:

  • 网站整体不校验csrf,就单单几个视图函数需要校验
  • 网站整体校验csrf,就单单几个视图函数不校验

针对这两种情况Django内部有两个装饰器

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

以下为FBV

反之整个网站都不需要校验,只有装饰器装饰的函数需要校验。

以下为CBV

url(r'^csrf'/,views.MyCsrfToken.as_view())
from 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): V
        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')