Django Cross Site Request Forgery protection(csrf,csrf_token)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Lockey23/article/details/81949366

在构建Django应用的时候如果涉及到表单或者一些ajax数据的提交操作,就难免会遇到csrf的问题,一般来说按照官网解说示例走都没啥问题,但是!大部分人不看官网文档,东拼一点,西凑一点,应用是跑起来了,但是出现点相关问题又是摸不着头脑;而且有些时候就算在官网看文档也是找不对地方,比如文档版本和应用框架不匹配,导致部分操作根本对不上。

今天我看了一下官网对于Django 2.0中Cross Site Request Forgery protection的相关文档,在这里稍作整理,顺便说一下我们在构建django web应用的过程中经常会出现的csrf_token问题

首先说一下相关问题:

1、{% csrf_token %}标签未被解析/渲染出来,导致无法获取X-CSRFToken从而无法正确提交数据
2、X-CSRFToken获取错误导致无法正确提交数据

然后解答一下相关问题:

1.1、In the corresponding view functions, ensure that RequestContext is used to render the response so that {% csrf_token %} will work properly. If you’re using the render() function, generic views, or contrib apps, you are covered already since these all use RequestContext.(就是说在渲染的时候RequestContext要在渲染过程中被用到才能正确工作,如果使用render()、generic views、contrib apps那么就不用担心,因为已经默认使用)
1.2、 关于what is the difference between render(), render_to_response() and direct_to_template()?请访问https://stackoverflow.com/questions/5154358/django-what-is-the-difference-between-render-render-to-response-and-direc

2、确保你的{% csrf_token %}标签位置放对了,如果一个网页中可能会发送多个请求,请不要使用form然后把{% csrf_token %}标签包围在内,而是作为一个全局标签放到模块的起始位置

接下来进入官方文档的整理内容:

首先我们要明白什么是安全的http请求方法,什么是不安全的(The first defense against CSRF attacks is to ensure that GET requests (and other ‘safe’ methods, as defined by RFC 7231#section-4.2.1) are side effect free. Requests via ‘unsafe’ methods, such as POST, PUT, and DELETE…)

How to use it

The CSRF middleware is activated by default in the MIDDLEWARE setting. If you override that setting, remember that ‘django.middleware.csrf.CsrfViewMiddleware’ should come before any view middleware that assume that CSRF attacks have been dealt with.

If you disabled it, which is not recommended, you can use csrf_protect() on particular views you want to protect (see below).

In any template that uses a POST form, use the csrf_token tag inside the element if the form is for an internal URL, e.g.:

{% csrf_token %}
This should not be done for POST forms that target external URLs, since that would cause the CSRF token to be leaked, leading to a vulnerability.

In the corresponding view functions, ensure that RequestContext is used to render the response so that {% csrf_token %} will work properly. If you’re using the render() function, generic views, or contrib apps, you are covered already since these all use RequestContext.

对于AJAX请求要怎么做才能不出现csrf_token 失败的错误

对于发送的ajax请求必须要携带 CSRF token in as POST data with every POST request. For this reason, there is an alternative method: on each XMLHttpRequest, set a custom X-CSRFToken header to the value of the CSRF token. This is often easier, because many JavaScript frameworks provide hooks that allow headers to be set on every request.

下面说一种经常会用到对策,也是官网有的:

首先获取隐藏的X-CSRFToken值(Acquiring the token if CSRF_USE_SESSIONS is True):

var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();

Acquiring the token if CSRF_USE_SESSIONS is False:

// using jQuery
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');

然后将这个值设置到AJAX request中去:

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

这样后面无论你要发送多少个ajax请求都不用再管csrftoken的值了,就按照一般写法写OK的

$.ajax({
            type: "POST",  //提交方式
            url: '/url',//路径
            data: JSON.stringify(postData),//数据
            dataType: 'json',
            contentType: "application/json;charset=utf-8",
            success: function (response) {//返回数据根据结果进行相应的处理
            }
          }
         )

官网还好心提醒了一句:

If you’re using AngularJS 1.1.3 and newer, it’s sufficient to configure the $http provider with the cookie and header names:

$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';

Using CSRF in Jinja2 templates

Django’s Jinja2 template backend adds {{ csrf_input }} to the context of all templates which is equivalent to {% csrf_token %} in the Django template language. For example:

<form method="post">{{ csrf_input }}

The decorator method

使用csrf_protect decorator对特定的view应用csrf保护:

!!!Use of the decorator by itself is not recommended

from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect

@csrf_protect
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)

以上内容属于重点掌握,更多了解请去官网
https://docs.djangoproject.com/en/2.0/ref/csrf/

猜你喜欢

转载自blog.csdn.net/Lockey23/article/details/81949366