使用Nginx部署Django应用程序后,AJAX POST请求上的CSRF失败

时间:2018-11-20 13:33:42

标签: ajax django nginx csrf django-csrf

我使用Nginx和Gunicorn在VPS上部署Django 2.X博客。
当我通过Jquery AJAX将一些数据推送到Django后端时,出现了403 CSRF错误。我在Google上搜索了很多,但仍然不知道如何解决此问题。 我尝试使用相同的配置文件将它们部署到本地计算机上,一切正常

首先,我没有在Django CSRF_USE_SESSIONS = True中设置CSRF_COOKIE_HTTPONLY = Truesettings.py。因此它们都是默认值False

我已经从Django document复制了AJAX示例代码:

// get csrftoken via JavaScript
function getCookie(name) {
    let cookieValue = null
    if (document.cookie && document.cookie !== '') {
        let cookies = document.cookie.split(';')
        for (let i = 0; i < cookies.length; i++) {
            let 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
}

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method))
}

$.ajaxSetup({
    beforeSend: (xhr, settings) => {
        let $captchaMassage = $('#captcha-message')
        let csrftoken = getCookie('csrftoken')
        console.debug('csrftoken: ' + csrftoken)
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken)
        }
        if ($captchaMassage) {
            $captchaMassage.text('please wait...')
        }
    }
})

这是我的AJAX:

$.ajax({
        async: true,
        method: 'POST',
        url: '{% url "captcha_mine_taken_verification" %}',
        dataType: 'json',
        data: {'coinhive_captcha_token': token},
        success: result => {
            if (result.success) {
                console.debug(result.data)
                $captchaMassage.text('thanks to donate')
            } else {
                $captchaMassage.text('failure reason: ' + result.reason)
            }
        },
        error: () => {
            $captchaMassage.text('oh my god, something wrong...')
        }
    })

这是来自Chrome的请求标头:

POST /cloudsen_blog/captcha-mine/taken-verification HTTP/1.1
Host: 104.243.15.163
Connection: keep-alive
Content-Length: 55
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://104.243.15.163
X-CSRFToken: null           <<<< this always null
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://104.243.15.163/cloudsen_blog/blog/article/5
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6

这是我的Gunicorn配置文件:

import multiprocessing

# unix domain socket
# bind = 'unix:/home/cloudsen/work/deploy/webservers/sockets/nginx-gunicorn.sock'
# TCP
bind = '127.0.0.1:8000'
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'gevent'
errorlog = '/home/cloudsen/work/deploy/webservers/gunicorn/gunicorn.error.log'
accesslog = '/home/cloudsen/work/deploy/webservers/gunicorn/gunicorn.access.log'
proc_name = 'gunicorn_myblog'

这是我的Nginx配置文件:

worker_processes 2;
user cloudsen;
error_log /home/cloudsen/work/deploy/webservers/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    accept_mutex on;
    use epoll;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    sendfile off;
    access_log /home/cloudsen/work/deploy/webservers/nginx/access.log combined;
    # Arch Linux start
    types_hash_max_size 4096;
    server_names_hash_bucket_size 128;
    # Arch Linux end

    upstream app_server {
        #server unix:/home/cloudsen/work/deploy/webservers/sockets/nginx-gunicorn.sock fail_timeout=0;
        server 127.0.0.1:8000 fail_timeout=0;
    }

    server {
        listen 80 default_server;
        return 444;
    }

    server {
        listen 80;
        server_name 104.243.15.163;
        client_max_body_size 4G;
        keepalive_timeout 5;

        location /favicon.ico {
            access_log off;
            log_not_found off;
        }

        location /static/ {
            alias /home/cloudsen/work/python/project/RedQueen/collected_statics/;
        }

        location = / {
            rewrite ^ /cloudsen_blog;
        }

        location / {
            proxy_pass_header X-CSRFToken;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $http_host;
            proxy_set_header Cookie $http_cookie;
            proxy_redirect off;
            proxy_pass http://app_server;
        }
    }
}

此外,当我在Chrome中调试时,我发现我的AJAX没有发送CSRF-TOKEN,并且document.cookie始终为空字符串,因此getCookie('csrftoken')始终返回Null。但是我不知道为什么。

感谢帮助~~~

1 个答案:

答案 0 :(得分:0)

  

页面使用AJAX,没有任何HTML格式。
  页面通过AJAX发出POST请求,并且该页面没有带有csrf_token的HTML表单,该表单会导致发送所需的CSRF cookie。

解决方案:在发送页面的视图上使用sure_csrf_cookie()。 通过使用ensure_csrf_cookie()装饰器,终于解决了该问题。

from django.views.decorators.csrf import ensure_csrf_cookie

# use this on your view
@ensure_csrf_cookie
def go_article_detail_page(request: HttpRequest, article_pk: int):
    pass

现在,每当您访问该页面时,Django都会在csrftoken中发送一个document.cookie