CSRF 令牌上的 Django 错误

Posted

技术标签:

【中文标题】CSRF 令牌上的 Django 错误【英文标题】:Django bug on CRSF token 【发布时间】:2018-04-10 17:24:55 【问题描述】:

我使用 django 作为后端的 web API,使用 React JS 作为前端的 web UI。用户将从 Web UI 注册,该 UI 将向 django 发送 POST 请求以使用用户详细信息进行注册。我想用 CSRF 保护注册视图。因此,我提出了以下步骤。

首先,加载注册页面后,我会发出一个虚拟的GET 请求来存储csrf 令牌,代码如下。

handleSend()
  let req = 
    url: 'http://localhost:9000/vcubes/retrieve_token/',
    method : 'GET',
    withCredentials: true
  
  axios(req)

然后,当用户提交注册表单时,会触发另一个 POST 请求。

const data = JSON.stringify(
        'first_name': this.state.first_name,
        'last_name': this.state.last_name,
        'email': this.state.email,
        'contact_number': this.state.contact_number,
        'password1': this.state.password1,
        'password2': this.state.password2,
      )
      let req = 
        url: 'http://localhost:9000/vcubes/signup/',
        method : 'POST',
        headers: 
                'Content-Type': 'text/plain'
        ,
        data: data
      
      axios.defaults.headers.common['X-CSRF-TOKEN'] = this.getCookie('vcubes')

使用上面的代码,会先向django发送一个OPTIONS,然后在django返回一个200 OK之后,才会触发实际的POST请求。

惊喜! Django说我没有设置CSRF cookie。

Forbidden (CSRF cookie not set.): /vcubes/signup/
[30/Oct/2017 01:30:48] "POST /vcubes/signup/ HTTP/1.1" 403 2857

我的settings.py 在下面。 (我只展示了一些与 CORS 和 CSRF 相关的代码)

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'corsheaders',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'custom_user',
    'vcubes',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

CSRF_TRUSTED_ORIGINS = (
    'localhost:8000',
    '127.0.0.1:8000'
)

CSRF_COOKIE_NAME = 'vcubes'

CSRF_HEADER_NAME = 'HTTP_X_CSRF_TOKEN'

CORS_ORIGIN_ALLOW_ALL = True

CORS_ALLOW_CREDENTIALS = True

CORS_ORIGIN_WHITELIST = (
    'localhost:8000',
    '127.0.0.1:8000'
)


CORS_ALLOW_HEADERS = (
    'access-control-allow-credentials',
    'access-control-allow-origin',
    'access-control-request-method',
    'access-control-request-headers',
    'accept',
    'accept-encoding',
    'accept-language',
    'authorization',
    'connection',
    'content-type',
    'dnt',
    'credentials',
    'host',
    'origin',
    'user-agent',
    'X-CSRF-TOKEN',
    'X-CSRFToken',
    'x-requested-with',
)

在views.py中

@ensure_csrf_cookie
def retrieve_token(request):
    return HttpResponse(status=200)

def signup(request):
    print request.META
    form = UserCreationForm(json.loads(request.body))
    # user = form.save(commit=False)
    # user.is_active = False
    # user.save()
    #
    # mail_subject = 'Activate your account.'
    # message = render_to_string('acc_active_email.html', 
    #     'name': user.get_full_name(),
    #     'domain': 'localhost:9000',
    #     'uid': urlsafe_base64_encode(force_bytes(user.pk)),
    #     'token': account_activation_token.make_token(user),
    # )
    # to_email = user
    # email = EmailMessage(
    #     mail_subject, message, to=[to_email]
    # )
    # email.send()
    return HttpResponse(status=200)

我花了一整天的时间从 google 和 *** 找出问题,但几乎没有得到任何帮助。请赐教!

【问题讨论】:

CSRF_COOKIE_SECURE 也设置了吗? 如果你没有看到这篇文章,有很多你可以修复的,试试看this CSRF_COOKIE_SECURE 默认设置为 false,这对我来说很好 您能显示/vcubes/retrieve_token/ 的视图吗?另外,CSRF_header_NAME 应该全部大写。 @knbk CSRF_HEADER_NAME 也不起作用(调试过程中的错字哈哈)并且已经添加到retrieve_token 视图中 【参考方案1】:

您的 post 请求缺少 withCredentials: true,这意味着您的 CSRF cookie 未随请求一起发送。

【讨论】:

有效!为什么我必须与 axios.defaults.headers.common['X-CSRF-TOKEN'] = this.getCookie('vcubes') 一起发送 withCredentials: true ?如果没有后面的代码,请求标头中仍然有Cookie:vcubes=3HqESj1ea9Pw6gAah6CvlGOlvUDYURW1MBbwsXYpzIJWvXT6Sjiylpf2hDlSZgLh CSRF 保护始终要求您将令牌作为 cookie 发送。它还要求您在请求正文中或作为标头发送令牌。

以上是关于CSRF 令牌上的 Django 错误的主要内容,如果未能解决你的问题,请参考以下文章

Django 1.9 AJAX 表单 CSRF 令牌 403 错误 - “未设置 CSRF cookie”

禁止(CSRF 令牌丢失或不正确)Django 错误

部署到 AWS 服务器时 django 应用程序的 CSRF 令牌错误

Django:在所有响应上强制 CSRF 令牌

打开 Chrome 开发人员工具时出现 Django CSRF 令牌错误

Unity Web Request + Django:CSRF 令牌丢失或不正确