我真的被卡住了。这就是我想要做的。
一切都很好用于当地。一旦我将其转移到生产环境,我就开始收到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()
我意识到我可能会在这里混合问题,但任何可以帮助我解决问题的建议都是受欢迎的。
首先,你想使用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-auth和django-all-auth。然而,django-rest-auth包不是为浏览器前端设计的,并且强制使用令牌生成,而且你需要如上所述包装视图。另一方面,all-auth为JS客户端提供AJAX响应,可能是更好的选择。
到目前为止,解决此问题的最简单方法是提供来自同一域的所有内容。您可以将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中间件上设置一个断点,看看是什么让它失效。