让Django,VUE,CORS和CSRF使用真实世界的例子

问题描述 投票:4回答:2

我真的被卡住了。这就是我想要做的。

  1. 保持CSRF开启。 - 请不要告诉我把它关掉。
  2. 我有一个由Django和Django Rest Framework运行的API应用程序
  3. 我有一个Vue运行的前端应用程序
  4. 我已经安装了django-cors-headers来管理CORS

一切都很好用于当地。一旦我将其转移到生产环境,我就开始收到CSRF错误。一切都是如此。

我已经看到了各种各样的答案已经说明了从关闭CSRF到允许所有事情的所有事情。我想要做到这一点,而不仅仅是关闭并打开所有东西,最终得到一个安全漏洞。

所以,这就是我所拥有的。

安装:django-cors-headers django-rest-framework drf-nested-routers ...等等

我在api.websitename.com上运行了api,并且在websitename.com上运行了Vue.js应用程序。

GET请求工作得很好。 OPTION请求似乎有效。

任何有风险的请求都不起作用。

对于我的CORS,我在其他'corsheaders.middleware.CorsMiddleware',之前安装了MIDDLEWARE

然后我的CORS设置是:

CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
    '*.websitename.com',
)

我的CSRF设置是:

CSRF_TRUSTED_ORIGINS = [
    "api.websitename.com",
]

无论我如何玩这些,我最终都会遇到CSRF令牌错误。

我在Vue App.vue文件中尝试过这样做的方法:

mounted () {
  this.getCSRFToken()
},
methods: {
  getCSRFToken () {
    return axios.get('token/').then(response => {
      axios.defaults.headers.common['x-csrftoken'] = Cookies.get('csrftoken')
    }).catch(error => {
      return Promise.reject(error.response.data)
    })
  }
}

我的想法是,一旦APP在浏览器中加载,我就会得到一个CSRF令牌。但即便如此,当应用程序尝试执行除GET或OPTION之外的任何操作时,我的CSRF令牌错误都会失败。

这是返回令牌的视图,这是你的好奇心:

class CSRFTokenView(APIView):
    permission_classes = (permissions.AllowAny,)

    @method_decorator(ensure_csrf_cookie)
    def get(self, request):
        return HttpResponse()

我意识到我可能会在这里混合问题,但任何可以帮助我解决问题的建议都是受欢迎的。

django vue.js django-rest-framework django-csrf django-cors-headers
2个回答
2
投票

首先,你想使用SessionAuthentication

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
    )
}

除了匿名用户之外,这将强制实施CSRF(稍后会详细介绍)。对于浏览器前端,最简单的解决方案是在同一个域下同时拥有(浏览器)前端和后端 - 这可以让你避免使用CORS - 正如上面的评论所建议的那样。如果您有其他客户端,那么只需使用令牌(DRF令牌或JWT) - 但由于存在XSS攻击的危险,这些对于浏览器使用是不安全的(在localStorage中存储令牌本质上是不安全的)。

当您使用axios时,CSRF设置很容易:

import axios from 'axios'

axios.defaults.xsrfHeaderName = 'X-CSRFToken'
axios.defaults.xsrfCookieName = 'csrftoken'

因此,您应该强制执行CSRF的安全会话。几乎。引用上面的链接页面:

警告:创建登录页面时始终使用Django的标准登录视图。这将确保您的登录视图得到适当保护。

REST框架中的CSRF验证与标准Django的工作方式略有不同,因为需要同时支持基于会话和非会话的身份验证。这意味着只有经过身份验证的请求才需要CSRF令牌,并且可以在没有CSRF令牌的情况下发送匿名请求。此行为不适用于登录视图,登录视图应始终应用CSRF验证。

这是icky - 您要么只需要使用Django服务器端视图,这会使您的SPA设计更复杂一些,或者在DRF中重新创建登录和其他身份验证视图,但需要注意使用@csrf_protect方法装饰器对这些“匿名”强制执行CSRF观点。显然,对于使用令牌的客户端,这样的视图会中断,因此您可能希望为这些使用不同的端点(可能重用相同的基类)。因此,您的浏览器登录使用/ auth / browser / login /和您的移动登录/ auth / mobile / login /,前者使用@csrf_protect包装。

在研究contrib auth源代码后,应该从头开始重新创建登录和其他身份验证视图;对于香草要求,我会推荐预先存在的解决方案,如django-rest-authdjango-all-auth。然而,django-rest-auth包不是为浏览器前端设计的,并且强制使用令牌生成,而且你需要如上所述包装视图。另一方面,all-auth为JS客户端提供AJAX响应,可能是更好的选择。


0
投票

到目前为止,解决此问题的最简单方法是提供来自同一域的所有内容。您可以将CDN或代理直接/api调用到一台服务器,其余调用到前端服务器。这样就根本无需担心CORS。

为了实现这一点,我认为你只是缺少AXIOS配置中的withCredentials = true。当没有设置withCredentials时,Django要求发送CSRF cookie并且不通过交叉源请求发送cookie。

axios.interceptors.request.use(function (config) {
  config.withCredentials = true
  return config
})

另一个可能缺少的设置是Djano的SESSION_COOKIE_DOMAIN。你应该这样设置:

SESSION_COOKIE_DOMAIN=".mywebsite.com"

第一个点很重要,因为它告诉Django然后Web浏览器使用*.mywebsite.com的cookie,包括api.mywebsite.com

如果一切仍然失败,我建议在Django的CSRF中间件上设置一个断点,看看是什么让它失效。

© www.soinside.com 2019 - 2024. All rights reserved.