标头和 cookie 中的 CSRF 令牌在请求中不匹配

2024-05-12

我正在实现一个无状态 API,我的组织表示我需要防止 CSRF 攻击。

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

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

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

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

这是我的角度代码:

 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

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


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

这就是我解决问题的方法(某种程度上):

(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);
    }
}

这是我的 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 组成自己的令牌)。我们 建议该令牌是您网站身份验证的摘要 饼干加盐以增加安全性。

标头的名称可以使用 xsrfHeaderName 和 $httpProvider.defaults 处的 xsrfCookieName 属性 配置时、运行时的 $http.defaults 或每个请求的配置 目的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

标头和 cookie 中的 CSRF 令牌在请求中不匹配 的相关文章

随机推荐

  • 如何从 php 中的 .doc 文件获取页面数,以下代码适用于 .docx 而不是 .doc

    此代码工作 docx 格式文档 但我需要 doc 格式文档计数 php 中的页数 function CountPagesDocx filename zip new ZipArchive if zip gt open filename tru
  • 401 Unauthorized("detail":"未提供身份验证凭据。")

    我在后端使用 djoser 的身份验证 当我通过具有内容类型和授权标头的邮递员在 account me 发出获取请求时 我得到了正确的响应 但是当我尝试从我的角度客户端执行相同的请求时 我得到401 Unauthorized detail
  • .Net Core 中的脚手架以及解决方案中的多个项目

    我创建了一个针对 net461 的 Net Core MVC6 应用程序 我使用了一个我非常熟悉的项目结构 其中我将数据 模型和服务类放置在单独的类库项目中 并且 Web 项目引用这些项目 当我尝试搭建控制器时 我收到一条错误 指出我正在搭
  • __author__ 的起源是什么?

    使用私有元数据变量的约定在哪里 author 一个模块内部从何而来 This http mail python org pipermail python dev 2001 March 013328 htmlPython 邮件列表线程似乎暗示
  • 包含当月所有日期的 PHP 数组 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我正在尝试并且仍然想知道如何获得一个
  • 如何使用 TS 配置 CRA 以支持 nullish-coalescing-operator

    所以我开始了一个新的 CRA 项目 我正在使用 TS beta 来获得一些不错的功能 例如链接运算符 但我也想使用nullish coalescing operator ifExists elseUseThis 不幸的是它不能开箱即用 并告
  • 有效的 URL 分隔符

    我有一个很长的 URL 其中包含多个值 示例1 http www domain com list seach type 0 search period 1 search min 3000 search max 21000 search ar
  • 如何扩展和重写集合类中的 Add

    背景 我在 PowerShell 中有一个具有 4 个属性的数据对象 其中 3 个是字符串 第 4 个是哈希表 我想安排一个新类型 将其定义为该数据对象的集合 在这个集合类中 我希望强制执行一种特定的格式 这将使我在模块中其他地方的代码更加
  • 如何让导航栏在向下滚动页面时改变颜色?

    我希望导航栏是透明的 但是当您向下滚动页面时 它会变为红色 div class nav div class container div class logo a href img src RepublicSquare logo svg st
  • zend框架找不到模型类?

    Yall 我有一个简单的问题 这可能是一个简单的配置问题 但是 我定义了一个模型 当我尝试从控制器访问它时 失败 模型位于模型目录中 当我查看快速启动应用程序时 看来这应该可行 这是我的模型 我的控制器停止了 这是控制器代码
  • std::string 在 Visual Studio 上的具体行为?

    我有一个项目需要读取 写入大文件 我决定使用 ifstream read 将这些文件一次性放入内存中 放入 std string 中 这似乎是在 C 中执行此操作的最快方法 http insanecoding blogspot com 20
  • 如何在 Node.js 中打开 Windows-1255 编码文件?

    我有一个 Windows 1255 希伯来语 编码的文件 我希望能够在 Node js 中访问它 我尝试使用打开文件fs readFile 它给了我一个Buffer我无能为力 我尝试将编码设置为Windows 1255 但这没有被识别 我还
  • 多个 scala 库导致 intellij 出错?

    我正在使用 intellij 14 和 scala 2 11 6 使用 homebrew 安装并使用符号链接 ln s usr local Cellar scala 2 11 6 libexec src usr local Cellar s
  • 为什么 Clojure MySQL 查询结果中出现“M”

    我有一个返回一行的 Clojure 查询 下面是返回行 映射 的部分打印输出 employer percent 0 00M premium 621 44M 这两列在mysql表中分别是decimal 5 2 和decimal 7 2 为什么
  • 如何使用 ReactJS 使表中的列可以以两种方式排序

    我正在 ReactJS 中构建一个简单的应用程序 它通过调用某个 API 来使用 JSON 数组 然后我将数组的结果填充到表中 我现在想让表的列可排序 我理想的情况是同时进行升序和降序排序 一旦我单击标题 当它按升序排序时 它应该按降序排序
  • 如何在 FastAPI Swagger API 中按方法类型对方法进行排序?

    如何在 FastAPI Swagger 自动文档中设置 API 方法的排序顺序 我希望所有方法按类型分组 GET POST PUT DELETE 这个答案 https stackoverflow com questions 24951268
  • 检查当前语言是否存在项目?

    我有一个 Sitecore 解决方案 其中启用了 3 种不同的语言 页面顶部有每种语言的链接 单击此链接后 您将获得当前所在页面 采用所选语言 但并非所有页面都被翻译成所有语言 因此 如果我站在英语页面 x 上 并且此页面仅提供英语和德语版
  • 如何将任务添加到 gradle 中的主要“构建”任务

    当我尝试使用以下代码将任务添加到主构建任务时 rootProject tasks getByName build dependsOn mytask 当我跑步时它抱怨gradle w build输出 Where Build file line
  • Laravel中with()和compact()有什么区别

    功能有什么区别with and compact 在 Laravel 中这两个例子 示例1 return View make books index gt with booksList booksList 示例2 return View ma
  • 标头和 cookie 中的 CSRF 令牌在请求中不匹配

    我正在实现一个无状态 API 我的组织表示我需要防止 CSRF 攻击 我在网上找到了这个人的解决方案 并决定尝试实施仅客户端的方法 http blog jdriven com 2014 10 stateless spring securit