引入

在毕设的初期 csrf 的中间件 CsrfViewMiddleware 都是被我注视掉的,在之后了解了跨域攻击,认为防御跨域攻击还是需要的

CsrfViewMiddleware 防止跨域攻击的原理

Django 防止 CSRF 攻击的方法其实是在 cookie 中保存 csrftoken 信息,在 Form 请求或者 Ajax 请求中确认本次请求是否携带有相关参数 。

CsrfViewMiddleware 中存在 4 个函数

  • _accept csrf 验证成功,允许请求所返回的函数
  • _reject csrf 验证失败,拒绝请求,默认返回 403
  • process_view 用于验证返回的 csrf 值是否正确
  • process_response 用于在 Cookie 中设置 csrftoken

核心部分为 set_cookie

1
2
3
4
5
6
7
8
        response.set_cookie(settings.CSRF_COOKIE_NAME,
                            request.META["CSRF_COOKIE"],
                            max_age=settings.CSRF_COOKIE_AGE,
                            domain=settings.CSRF_COOKIE_DOMAIN,
                            path=settings.CSRF_COOKIE_PATH,
                            secure=settings.CSRF_COOKIE_SECURE,
                            httponly=settings.CSRF_COOKIE_HTTPONLY
                            )

请求部分

Form 部分

如果使用 Django 默认的 Form 可以知道实现原理其实是在 Form 中添加了一条 Hidden 的 input 用于传入 csrfmiddlewaretoken 参数

1
<input type='hidden' name='csrfmiddlewaretoken' value='2nRvto2bluWeQ8ETz37b4wFc6OQSvgNLgjUyExNoznPs8pAXiYrwd5VSl7v8T5IR' />

伪 Form 请求

在伪装的请求中添加 csrfmiddlewaretoken

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
$.extend({
            StandardPost: function (url, args) {
                var body = $(document.body),
                    form = $("<form method='post'></form>"),
                    input;
                form.attr({"action": url});
                input = $("<input type='hidden'>");
                input.attr({"name": "csrfmiddlewaretoken"});
                input.val(csrftoken);
                form.append(input);
                $.each(args, function (key, value) {
                    input = $("<input type='hidden'>");
                    input.attr({"name": key});
                    input.val(value);
                    form.append(input);
                });

                form.appendTo(document.body);
                form.submit();
                document.body.removeChild(form[0]);
            }
        });

Ajax 部分

为了方便使用,可以在 base 中添加如下 javascript 代码,用于在所有的 ajax 请求中设置 “X-CSRFToken” 请求头

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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]);
                    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) {
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }
        $.ajaxSetup({
            beforeSend: function (xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            }
        });

process_view 后端校验部分

通过校验从请求中返回的 csrf 值是否和服务端保存的设置的 csrf 值的一致性,从而决定返回 _accept 或 _reject

在使用时发现返回 ResponseTemplate 的 Form 中会有 csrftoken 值,但是 render 中的 form 没有值暂时不知道产生原因。