让 Django、VUE、CORS 和 CSRF 与真实世界的例子一起工作

Dav*_*win 7 django django-csrf django-rest-framework vue.js django-cors-headers

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

  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 在 api.websitename.com 上运行,Vue.js 应用程序在 websitename.com 上运行。

GET 请求效果很好。OPTION 请求似乎有效。

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

对于我的 CORS,我'corsheaders.middleware.CorsMiddleware',在其他MIDDLEWARE.

然后我的 CORS 设置是:

CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
    '*.websitename.com',
)
Run Code Online (Sandbox Code Playgroud)

我的 CSRF 设置是:

CSRF_TRUSTED_ORIGINS = [
    "api.websitename.com",
]
Run Code Online (Sandbox Code Playgroud)

无论我如何使用这些,最终都会出现 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)
    })
  }
}
Run Code Online (Sandbox Code Playgroud)

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

这是返回令牌的视图,以防您的古玩:

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

    @method_decorator(ensure_csrf_cookie)
    def get(self, request):
        return HttpResponse()
Run Code Online (Sandbox Code Playgroud)

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

小智 5

首先,您要使用SessionAuthentication

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
    )
}
Run Code Online (Sandbox Code Playgroud)

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

当您使用 axios 时,CSRF 设置非常简单:

import axios from 'axios'

axios.defaults.xsrfHeaderName = 'X-CSRFToken'
axios.defaults.xsrfCookieName = 'csrftoken'
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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


kic*_*hik 1

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

为了让这个工作正常,我认为你只是缺少withCredentials = trueAXIOS 配置。Django 要求发送 CSRF cookie,如果withCredentials未设置,则不会通过跨源请求发送 cookie。

axios.interceptors.request.use(function (config) {
  config.withCredentials = true
  return config
})
Run Code Online (Sandbox Code Playgroud)

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

SESSION_COOKIE_DOMAIN=".mywebsite.com"
Run Code Online (Sandbox Code Playgroud)

第一个点很重要,因为它告诉 Django 以及 Web 浏览器使用 cookie 来*.mywebsite.com包含api.mywebsite.com.

如果仍然失败,我建议在 Django 的 CSRF 中间件上设置一个断点,看看缺少什么才能使其正常工作。