CSRF和CORS与Django(REST框架)

时间:2018-03-12 16:28:43

标签: javascript django django-rest-framework cors csrf

我们正在将我们的前端转移到一个单独的项目(Django之外)。这是一个Javascript单页面应用程序。

其中一个原因是让我们的前端开发人员更容易完成他们的工作,而不必在本地运行整个项目 - 包括API。相反,我们希望他们能够与我们设置的测试API进行通信。

我们设法解决了大部分CORS / CSRF问题。但是现在我们遇到了一些我无法在任何地方找到解决方案的东西,尽管阅读了大量文档和SO答案。

前端和API由不同的域提供(在开发期间localhosttest-api.example.com)。到目前为止,虽然从同一个域提供服务,但前端已经能够从API(Django)设置的csrftoken cookie中获取CSRF令牌。但是,当从不同的域提供服务时,前端(localhost)无法访问API的cookie(api-test.example.com)。

我正试图想办法解决这个问题,以某种方式将CSRF令牌传递给前端。 The Django docs recommend to set a custom X-CSRFToken header用于AJAX请求。如果我们在每个响应中以类似方式提供CSRF令牌作为标头并且(通过Access-Control-Expose-Headers)允许前端读取此标头,我们是否会破坏CSRF保护?

鉴于我们已经为API正确设置了CORS(即只允许某些域对API进行跨域请求),第三方站点上的JS应该无法读取此响应头,因此无法读取在我们的用户背后做出妥协的AJAX请求,对吧?还是我错过了一些重要的东西?

还是有另一种更好的方式来实现我们想要的目标吗?

2 个答案:

答案 0 :(得分:2)

假设您已安装corsheaders。编写Django中间件并将其包含在MIDDLEWARE设置中:

from django.utils.deprecation import MiddlewareMixin

class CsrfHeaderMiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        if "CSRF_COOKIE" in request.META:
            # csrfviewmiddleware sets response cookie as request.META['CSRF_COOKIE']
            response["X-CSRFTOKEN"] = request.META['CSRF_COOKIE']
        return response

在您的设置中公开标题:

 CORS_EXPOSE_HEADERS = ["X-CSRFTOKEN"]

当您从JS进行GET API调用时,您应该从响应标头获取X-CSRFTOKEN,当您POST {{1时,请继续将其包含在请求标头中}} PUT PATCH请求。

答案 1 :(得分:1)

我一开始并不理解你的问题,所以请允许我总结一下:你无法从客户端的cookie获取CSRF令牌,因为同源策略阻止你访问跨域cookie (即使使用CORS)。因此,您建议服务器在自定义标头中将Cookie传输到客户端,并且想知道它是否安全。

现在,如果您不使用Cookie,文档会对如何传输令牌提出建议:put it in the response body。例如,您可以使用自定义meta标记。在安全方面,我倾向于使用推荐的解决方案,而不是相信我自己对新事物的分析。

除此之外,我不会发现您提出的任何安全问题。同源策略将阻止第三方站点像正文一样读取标题,您可以选择使用CORS Access-Control-Expose-Headers标题从客户端域读取它们。

您可能会发现this answer很有趣,因为它列出了各种CSRF令牌方案的优缺点。它包括使用自定义响应标头,并且 - 至于您的问题 - 确认:"如果恶意用户尝试在上述任何方法中读取用户的CSRF令牌,那么这将是被同源政策阻止"。

(您可能想要查看您的SPA是否需要Django&CSRFF保护。例如,请参阅this analysis。但这不在本问题的范围之内。 )