【问题标题】:CSRF Token in header and cookie do not match in requests请求中的标头和 cookie 中的 CSRF 令牌不匹配
【发布时间】:2015-02-13 21:15:00
【问题描述】:

我正在实施一个无状态 API,我的组织说我需要防范 CSRF 攻击。

我在网上找到了这个人的解决方案,并决定尝试实施仅客户端的方法:http://blog.jdriven.com/2014/10/stateless-spring-security-part-1-stateless-csrf-protection/

以下是网站所说的无状态解决方案(以防网站出现故障):

  1. 客户端生成的 CSRF 令牌。让客户端在 Cookie 和自定义 HTTP 中生成并发送相同的唯一秘密值 标题。考虑到一个网站只允许读/写一个 Cookie 对于自己的域,只有真实站点可以在两者中发送相同的值 标题。使用这种方法,您的服务器所要做的就是检查是否 在无状态的每个请求的基础上,这两个值是相等的!

不幸的是,它不起作用。我的标头值永远不会与我的 cookie 值匹配,在某些情况下,我的标头似乎只是匹配 cookie 值之后的一个请求。

这是我的 Angular 代码:

 app.config(['$httpProvider', function ($httpProvider) {
     //fancy random token
     function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e16]+1e16).replace(/[01]/g,b)};

     $httpProvider.defaults.xsrfHeaderName = 'X-CSRF-TOKEN';
     $httpProvider.defaults.xsrfCookieName = 'CSRF-TOKEN';

     $httpProvider.interceptors.push(function () {
         return {
             'request': function (config) {
                 document.cookie = 'CSRF-TOKEN=' + b();
                 return config
             }
         };
     });
 }]);

以下是正在发送的 CSRF 值的一些示例。

 CSRF-TOKEN=d25cf03a985d575ad48a863eac91467666
 X-CSRF-TOKEN:fa1f165df8b27195a90f5e7841108f4e42

 CSRF-TOKEN=d25cf03a985d575ad48a863eac91467666
 X-CSRF-TOKEN:fa1f165df8b27195a90f5e7841108f4e42

 CSRF-TOKEN=9c8dd46ed06c250b707ac0cb80a08a23ac
 X-CSRF-TOKEN:d25cf03a985d575ad48a863eac91467666

 CSRF-TOKEN=eb407a0303c21173fe4d0ae03c97eaea6d
 X-CSRF-TOKEN:0cf066bf83e50b5c74cb932ab8a47c94e8

 CSRF-TOKEN=506355a940a2ac5b48f363712b34570d73
 X-CSRF-TOKEN:eb407a0303c21173fe4d0ae03c97eaea6d

这里发生了什么?我觉得我正在做那个人的解决方案中的所有事情,但结果却很奇怪。

【问题讨论】:

  • > 考虑到一个网站只允许为自己的域读/写一个Cookie,只有真实的站点可以在两个标头中发送相同的值。当多个网站共享同一个域时会发生什么?这是否会使您的网站免受共享它的其他人之一的攻击?我想如果您确定您的网站将是该域中唯一的网站,那么这种方法可能会奏效。

标签: angularjs cookies http-headers csrf


【解决方案1】:

我最终没有发送客户端随每个请求创建的随机令牌。我无法让它工作。

这就是我解决问题的方法(有点):

(1) 在每个请求(包括第一个请求)中,我从我的 API 发回响应标头中的 cookie,其名称为“XSRF-TOKEN”以及与之关联的随机值。这是 AngularJS 在使用其 CSRF 保护时默认寻找的名称。

(2) 在收到该令牌后的请求中会发生什么,AngularJS 使用该令牌的值在名为“XSRF-TOKEN”的请求标头中发送一个 cookie,以及一个名为“X-XSRF-TOKEN”的标头该令牌的价值也是如此。

所以我的 API 正在处理随机化 XSRF 令牌,而我的应用程序仍然是无状态的。我正在使用 Web API,并且正在使用全局过滤器来处理此 XSRF 令牌创建。下面是我执行此操作的代码(在 C# 中)。我不再有任何代码在 UI 中处理这个问题(因为它似乎不需要):

public class ValidateAntiForgeryToken : ActionFilterAttribute
{
    private const string XsrfCookieName = "XSRF-TOKEN";

    private const string XsrfHeaderName = "X-XSRF-TOKEN";

    private const string CsrfTokenSalt = "RANDOM SALT";


    public override void OnActionExecuting(HttpActionContext filterContext)
    {
        string requestMethod = filterContext.Request.Method.Method;

        Boolean isValid = true;

        if (requestMethod != "GET")
        {
            var headerToken = filterContext.Request.Headers.Where(x => x.Key.Equals(XsrfHeaderName, StringComparison.OrdinalIgnoreCase))
                .Select(x => x.Value).SelectMany(x => x).FirstOrDefault();

            var cookieToken = filterContext.Request.Headers.GetCookies().Select(x => x[XsrfCookieName]).FirstOrDefault();

            // check for missing cookie or header
            if (cookieToken == null || headerToken == null)
            {
                isValid = false;
            }

            // ensure that the cookie matches the header
            if (isValid && !String.Equals(headerToken, cookieToken.Value, StringComparison.OrdinalIgnoreCase))
            {
                isValid = false;
            }

            if (!isValid)
            {
                filterContext.Response = filterContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                filterContext.Response.ReasonPhrase = "Unauthorized to make that request.";
                return;
            }
        }

        base.OnActionExecuting(filterContext);
    }


    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        string textToHash = RandomStringGeneration();
        string cookieText = HashService.HashText(textToHash, CsrfTokenSalt);

        var cookie = new CookieHeaderValue(XsrfCookieName, HttpUtility.UrlEncode(cookieText));

        /* don't use this flag if you're not using HTTPS */
        cookie.Secure = true;      
        cookie.HttpOnly = false; // javascript needs to be able to get this in order to pass it back in the headers in the next request

        /* if you have different environments on the same domain (which I did in one application using this code) make sure you set the path to be ApplicationPath of the request. Case sensitivity does matter in Chrome and IE, so be wary of that. */
        cookie.Path = "/";

        actionExecutedContext.Response.Headers.AddCookies(new[] { cookie });

        base.OnActionExecuted(actionExecutedContext);
    }
}

这是我的 HashService.HashText() 代码:

public class HashService
{
    public static string HashText(string text, string salt)
    {
        SHA512Managed hashString = new SHA512Managed();

        byte[] textWithSaltBytes = Encoding.UTF8.GetBytes(string.Concat(text, salt));
        byte[] hashedBytes = hashString.ComputeHash(textWithSaltBytes);

        hashString.Clear();

        return Convert.ToBase64String(hashedBytes);
    }
}

希望这对将来使用无状态应用程序的人有所帮助。不幸的是,发回的令牌仅在 cookie 值和标头值中与自身进行比较。这是我现在能够验证它的唯一方法(我觉得这很安全)。我可能会为 XSRF 保护创建一个全新的表,并使用它来验证令牌确实是用户应该使用的令牌。这是我可以全神贯注地保持 API 无状态的唯一方法。

在阅读 AngularJS 的 $http 文档后,我偶然发现了这个解决方案,其中规定:

跨站点请求伪造 (XSRF) 保护:XSRF 是一种技术 未经授权的网站可以获取您用户的私人数据。角 提供了一种对抗 XSRF 的机制。在执行 XHR 请求时, $http 服务从 cookie 中读取令牌(默认为 XSRF-TOKEN) 并将其设置为 HTTP 标头 (X-XSRF-TOKEN)。由于只有 JavaScript 在您的域上运行的可以读取 cookie,您的服务器可以是 确保 XHR 来自在您的域上运行的 JavaScript。这 跨域请求不会设置header。

要利用这一点,您的服务器需要在 在第一个 HTTP 上称为 XSRF-TOKEN 的 JavaScript 可读会话 cookie 获取请求。在随后的 XHR 请求中,服务器可以验证 cookie 匹配 X-XSRF-TOKEN HTTP 标头,因此请确保 只有在您的域上运行的 JavaScript 才能发送请求。 每个用户的令牌必须是唯一的,并且必须由 服务器(以防止 JavaScript 组成自己的令牌)。我们 建议令牌是您网站身份验证的摘要 带有盐的 cookie 以增加安全性。

可以使用 xsrfHeaderName 和 $httpProvider.defaults 的 xsrfCookieName 属性位于 config-time, $http.defaults at run-time, or per-request config 对象。

【讨论】:

  • 如何在 Angular 13 中做到这一点?,我尝试了很多方法,仍然得到错误 404。
猜你喜欢
  • 2015-12-20
  • 2014-11-04
  • 1970-01-01
  • 2019-01-25
  • 1970-01-01
  • 1970-01-01
  • 2021-04-14
  • 2017-11-29
  • 1970-01-01
相关资源
最近更新 更多