Rya*_*ing 5 cookies csrf http-headers angularjs
我正在实现无状态API,我的组织说我需要防范CSRF攻击.
我在网上发现了这个人的解决方案,并决定尝试实施一种仅限客户端的方法:http://blog.jdriven.com/2014/10/stateless-spring-security-part-1-stateless-csrf-protection/
以下是该网站为无状态解决方案所做的事情(如果网站出现故障):
- 客户端生成的CSRF-TOKENS.让客户端在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
             }
         };
     });
 }]);
Run Code Online (Sandbox Code Playgroud)
以下是正在发送的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
Run Code Online (Sandbox Code Playgroud)
这里发生了什么?我觉得我正在做那个家伙解决方案中的所有事情,但结果却是奇怪的结果.
我最终没有发送客户端在每个请求中创建的随机令牌。我无法让它发挥作用。
这就是我解决问题的方法(某种程度上):
(1) 对于每个请求(包括第一个请求),我会从 API 的响应标头中发回一个名为“XSRF-TOKEN”的 cookie 以及与其关联的随机值。这是 AngularJS 在使用 CSRF 保护时默认寻找的名称。
(2) 收到该令牌后,请求中会发生什么情况,AngularJS 使用该令牌的值在名为“XSRF-TOKEN”的请求标头中发送 cookie,并在名为“X-XSRF-TOKEN”的标头中发送该令牌的值以及。
因此,我的 API 正在处理 XSRF 令牌的随机化,并且我的应用程序仍然是无状态的。我正在使用 Web API,并使用全局过滤器来处理此 XSRF 令牌创建。下面是我用于执行此操作的代码(C# 语言)。我不再有任何代码在用户界面中处理这个问题(因为它似乎不需要):
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);
    }
}
Run Code Online (Sandbox Code Playgroud)
这是我的 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);
    }
}
Run Code Online (Sandbox Code Playgroud)
希望这对将来使用无状态应用程序的人有所帮助。不幸的是,发回的令牌仅在 cookie 值和标头值中与其自身进行比较。这是我现在能够验证它的唯一方法(我觉得这是相当安全的)。我可能会创建一个全新的 XSRF 保护表,并使用它来验证令牌确实是用户应该使用的令牌。这是我能够保持 API 无状态的唯一方法。
在阅读 AngularJS 的 $http 文档后,我偶然发现了这个解决方案,其中规定:
跨站点请求伪造 (XSRF) 保护:XSRF 是一种技术,未经授权的站点可以通过该技术获取用户的私人数据。Angular 提供了一种对抗 XSRF 的机制。执行 XHR 请求时,$http 服务从 cookie 中读取令牌(默认情况下为 XSRF-TOKEN),并将其设置为 HTTP 标头 (X-XSRF-TOKEN)。由于只有在您的域上运行的 JavaScript 才能读取 cookie,因此您的服务器可以确信 XHR 来自在您的域上运行的 JavaScript。跨域请求不会设置 header。
为了利用这一点,您的服务器需要在第一个 HTTP GET 请求上在 JavaScript 可读会话 cookie 中设置一个名为 XSRF-TOKEN 的令牌。在后续 XHR 请求中,服务器可以验证 cookie 是否与 X-XSRF-TOKEN HTTP 标头匹配,因此确保只有在您的域上运行的 JavaScript 才能发送请求。每个用户的令牌必须是唯一的,并且必须可由服务器验证(以防止 JavaScript 生成自己的令牌)。我们建议该令牌是您网站的身份验证 cookie 的摘要,并带有盐以增加安全性。
标头的名称可以使用配置时的 $httpProvider.defaults、运行时的 $http.defaults 或每个请求的配置对象的 xsrfHeaderName 和 xsrfCookieName 属性来指定。
|   归档时间:  |  
           
  |  
        
|   查看次数:  |  
           5367 次  |  
        
|   最近记录:  |