CORS,Ajax和CSRF

时间:2014-02-10 23:18:31

标签: jquery ajax cookies cors django-csrf

django应用程序中的CSRF预防支持通过cookie将CSRF令牌发送到客户端,并通过标头(X-CSRFToken)或cookie从客户端接受CSRF令牌。这适用于非CORS,非AJAX Web应用程序。但是,如果你a)拥有一个通过AJAX与服务器通信的单页Web应用程序,并且b)单页webapp托管在与服务器(CORS)不同的域中,那么它似乎不起作用。

问题在于,由于CORS限制,单页webapp(来自domain1)无法使用xhr.getResponseHeader或getCookie读取服务器域(domain2)cookie。 javascript webapp如何将适当的CSRF令牌发送到服务器,因为它无法读取cookie?

xhr.getResponseHeader api被限制检索Set-Cookie或Set-Cookie2标头(按规范),并且各种支持CORS的浏览器似乎强制执行此限制。类似地,getCookie JS函数将读取webapp域(domain1)中的所有非httpOnly cookie,但不会读取服务器在其域(domain2)中设置的cookie。

这在非CORS情况下不是问题,但在我们的应用程序中,我们希望在与客户端webapp不同的域中托管API。有什么建议?

2 个答案:

答案 0 :(得分:0)

我当时正在做同样的事情,这让我很生气。我正在使用MVC WEB API,但我认为有类似的东西会阻止你。

我的问题至少是OPTIONS调用服务器检查服务器是否允许发送自定义标头失败,因此从未进行实际的GET / POST调用。您必须覆盖服务器上的选项请求以允许此操作。当然,允许从您呼叫的站点到服务器的orgin。

以下是一篇关于ASP.NET的文章,但我认为这有助于您理解该问题,即使它是ASP.NET。

http://www.jefclaes.be/2012/09/supporting-options-verb-in-aspnet-web.html

希望这有帮助。

答案 1 :(得分:0)

我认为我遇到了同样的问题,并通过服务器端的东西和客户端的东西来解决它。服务器端(Django-Python):

origin = request.META.get('HTTP_ORIGIN', None)
if origin and origin in settings.safe_origins:
    response['Access-Control-Allow-Origin'] = origin
    response['Access-Control-Allow-Credentials'] = 'true'
    response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
    response['Access-Control-Allow-Headers'] = request.META.get('Access-Control-Allow-Headers', 'x-requested-with, X-CSRFToken',)
    response['Access-Control-Max-Age'] = 15
    response['Allow'] = 'GET, POST, PUT, DELETE, OPTIONS'

启动时的客户端:

$.ajaxPrefilter(function(options, originalOptions, jqXHR)
{
    options.crossDomain =
    {
        crossDomain: true
    };
    options.xhrFields =
    {
        withCredentials: true
    };
});

function csrfSafeMethod(method)
{
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup(
{
    crossDomain: false, // obviates need for sameOrigin test
    beforeSend: function(xhr, settings)
    {
        if (!csrfSafeMethod(settings.type))
        {
            xhr.setRequestHeader("X-CSRFToken", csrf);
        }
    }
});

然后在$ .ajax调用自己的好措施:

$.ajax(
{
    type: "POST",
    url: theUrl,
    data: theData,
    contentType: 'application/x-www-form-urlencoded',
    dataType: 'json',
    xhrFields:
    {
       withCredentials: true
    }
});

对我来说,我失踪的东西和我的头靠在墙上的是这一部分:

xhrFields:
{
   withCredentials: true
}

希望这有助于某人。