即使 cookie 在开发人员工具中列出且 httpOnly 标志设置为 false,访问 document.cookie 也会返回空字符串

2024-04-28

有时*,访问时document.cookie在登录页面中,我得到一个空字符串,即使:

  1. cookie 列在 Chrome 和 Firefox 开发者工具中,
  2. 我感兴趣的 cookie 的 httpOnly 标志设置为 false,
  3. 我感兴趣的 cookie 路径设置为“/”。

期望的行为

我的 React 单页应用程序 (SPA) 有一个登录页面,其中包含<form />用于将登录凭据发送到后端的元素。当收到后端的响应并且身份验证成功时,我检查身份验证 cookie 是否已正确设置。如果是这种情况,将触发重定向,显示登录用户的内容。

实际行为

不幸的是,在像15%登录尝试次数,document.cookie返回一个空字符串,以防止重定向并使用户保持在登录页面上。紧迫F5并不能解决问题,但在成功登录请求后手动替换 url 路径时(例如更新 'www.website.tld/login' 到 'www.website.tld/start')用户将被转发到仅适用于登录用户的所需页面。

我无法手动重现该错误。这似乎是随机发生的。但是当它发生时,我查看开发人员控制台,我可以看到来自后端的所有 cookie(设置正确)。

附加信息

  • django 服务器在后端运行
  • 所需的 cookie 设置为response.set_cookie('key', 'value', secure=False httponly=False, samesite='strict')
  • JS 库(axios、react-router)

Related:

  • JS中无法从document.cookie访问cookie,但浏览器显示cookie存在 https://stackoverflow.com/questions/17508027/cant-access-cookies-from-document-cookie-in-js-but-browser-shows-cookies-exist(仅限http)
  • 无法在JS中使用document.cookie访问cookie https://stackoverflow.com/questions/6217795/cant-access-a-cookie-using-document-cookie-in-js(仅限http)

登录页面 (JSX)

    import React, { useState } from "react";
    import { Redirect } from "react-router-dom";
    import axios from "axios";

    /**
     * We're using cookies.js to read cookies.
     * Source: https://github.com/madmurphy/cookies.js
     */
    function hasItem(sKey) {
      return new RegExp(
        "(?:^|;\\s*)" +
          encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") +
          "\\s*\\="
      ).test(document.cookie);
    }

    export const LoginPage = () => {
      const [isAuthenticated, setIsAuthenticated] = useState(false);
      const [username, setUsername] = useState("");
      const [password, setPassword] = useState("");

      function handleSubmit(e) {
        e.preventDefault();

        function onSuccess(response) {
          // handle response
          // [...]

          // sometimes console.log(document.cookie) returns empty string
          if (hasItem("auth_cookie")) {
            setIsAuthenticated(true);
          } else {
            console.warn("Cookie not found!");
          }
        }

        function onFailure(error) {
          // handle error
        }

        const conf = {
          headers: new Headers({
            "Content-Type": "application/json; charset=UTF-8",
            Origin: window.location.origin
          })
        };

        axios
          .post("/api/login/", { username, password }, conf)
          .then(response => {
            onSuccess(response);
          })
          .catch(error => {
            onFailure(error);
          });
      }

      if (isAuthenticated) {
        return <Redirect to="/start" />;
      }

      return (
        <div className="login-page">
          <form
            name="login-form"
            method="post"
            onSubmit={e => handleSubmit(e)}
            action="api/login"
            target="hiddenFrame"
          >
            <iframe className="invisible-frame" src="" name="hiddenFrame" />
            <div>
              <label htmlFor="username">Email</label>
              <input
                name="username"
                type="text"
                onChange={e => setUsername(e.target.value)}
              />
            </div>
            <div>
              <label htmlFor="password">Password</label>
              <input
                name="password"
                type="password"
                onChange={e => setPassword(e.target.value)}
              />
            </div>

            <button type="submit">Submit</button>
          </form>
        </div>
      );
    };

路由 (JSX)

    import React from "react";
    import { Route, Redirect } from "react-router-dom";

    const RootLayout = () => {
      return (
        <div className="root-layout">
          <Switch>
            <PublicRoute path="/login" component={LoginPage} />
            <PrivateRoute path="/" component={App} />
          </Switch>
        </div>
      );
    };

    /**
     * Component that handles redirection when user is logged in already
     */
    const PublicRoute = ({ component: ChildComponent, ...remainingProps }) => {
      let isAuthenticated = hasItem("auth_cookie");
      return (
        <Route
          render={props =>
            isAuthenticated ? <Redirect to="/" /> : <ChildComponent {...props} />
          }
          {...remainingProps}
        />
      );
    };

    /**
     * Component that handles redirection when user has been logged out.
     * E.g. when authentication cookie expires.
     */
    const PrivateRoute = ({ component: ChildComponent, ...remainingProps }) => {
      let isAuthenticated = hasItem("auth_cookie");
      return (
        <Route
          render={props =>
            !isAuthenticated ? (
              <Redirect to="/login" />
            ) : (
              <ChildComponent {...props} />
            )
          }
          {...remainingProps}
        />
      );
    };

    const App = () => (
      <Switch>
        <Route exact path="/" render={() => <Redirect to="/start" />} />
        <Route exact path="/start" component={StartPage} />
        <Route exact path="/blog" component={BlogPage} />
        {/*...*/}
      </Switch>
    );

* I know, that's probably not how a post should start...


你遇到了一个问题sameSite饼干。看SameSite cookies 解释 https://web.dev/samesite-cookies-explained/解释一下:

如果您将 SameSite 设置为 Strict,您的 cookie 将仅在第一方上下文中发送。 [...] 当用户访问您的网站时,cookie 将按预期随请求一起发送。然而,当通过链接进入您的网站时,例如从另一个网站或通过朋友的电子邮件,在该初始请求中,将不会发送 cookie。当您拥有与始终位于初始导航后面的功能相关的 cookie(例如更改密码或进行购买)时,这很好,但对于 promo_shown 来说限制性太大。如果您的读者通过链接进入网站,他们希望发送 cookie,以便应用他们的偏好。

现在你至少有两个选择:

  • 推荐:保留samesite=strict并重构您的客户端代码。前端根本不需要访问身份验证cookie,因此您可以设置httponly=True。然后引入一个后端 API,该 API 根据客户端代码的请求来验证 cookie。这为您提供了额外的优势,不易受到 XSS 攻击,因为前端代码无法访问身份验证 cookie。
  • 不推荐:设置samesite to none or lax.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

即使 cookie 在开发人员工具中列出且 httpOnly 标志设置为 false,访问 document.cookie 也会返回空字符串 的相关文章

  • 每次用户在地址栏中按 Enter 时,Firefox 插件都会执行某些操作

    我正在尝试编写一个扩展程序 用于监视每次有人在使用地址栏时按下回车键时的情况 步骤将类似于 用户在地址栏中输入一堆文本并按 Enter 键 我的插件启动并接收用户输入的内容 然后我的插件决定如何处理用户输入的字符串 我通过使用在步骤 2 中
  • 无法读取未定义错误的属性“匹配”

    我试图在 React JS 前端显示一些文本来代替个人资料图像 当它不可用时 基本上 我将当前客户名称传递给一个函数 该函数提取名称中所有单词的第一个字符 我能够仅显示名称 但是当我执行函数调用时 出现 无法读取未定义的属性 匹配 错误 并
  • 检测 SVGAnimatedString 的类名

    我在构建 SVG 地图时遇到问题 触发的功能 g 上的 onmouseover 不起作用 我当时用的 window onmouseover function e console log e target className 查看类名是否有问
  • 正则表达式没有按预期工作?

    我有这个正则表达式 new RegExp a z 0 9 ig 我正在测试一个不应该工作的字符串 vc 但它确实通过了测试 而且它不应该 new RegExp a z 0 9 ig test vc true 但如果我删除其中一个 or or
  • React 状态总是从 Fabricjs 的回调中返回先前(或初始)状态

    下面的代码是我的最小问题重现组件 它初始化织物画布 并处理 模式 状态 模式状态决定画布是否可以编辑 并且一个简单的按钮控制该状态 问题是 即使mode setMode工作正常 意思是 单击按钮后组件分析器显示正确的状态 按钮内的文本也显示
  • 如何在 Django 1.4 中自定义管理过滤器

    我是 Python 和 Django 开发的新手 我从社区提供的易于阅读的示例中学到了很多东西 但最近我想为 Django 附带的管理控制台实现一个自定义的管理过滤器 我进行了很多搜索 只发现了一些过时的方法来完成它 例如 Django 1
  • Django - 该进程无法访问该文件,因为该文件正在被另一个进程使用

    我正在尝试在 Windows 10 上运行 Django 我是 Django 的新手 我正在使用 Compressor Toolkit 我的问题是 我可以运行 manage py 但本地主机说 base html 第 9 行出错该进程无法访
  • 在浏览器中打开的 .mhtml 文件中填写输入

    我想对 mhtml 文件运行 e2e 测试 即填写表格 在 mhtml 文件上查看和提取数据效果非常好 但我无法填写任何内容input字段 既不是手动也不是通过木偶操作者 你可以用这个试试 mhtml 文件 https gist githu
  • 为什么发送 fetch() 时我的响应数据未定义?

    我正在尝试在客户端使用 fetch 将数据发布到我的 NodeJS 服务器或从我的 NodeJS 服务器发布数据 服务器很好地收到了 post 请求 我能够记录 req 变量 但是当我 res send any data 时 客户端无法检测
  • 运行 Django 测试时如何将 DEBUG 设置为 True?

    我目前正在运行一些 Django 测试 看起来DEBUG False默认情况下 有没有办法运行我可以设置的特定测试DEBUG True在命令行还是在代码中 对于测试用例内的特定测试 您可以使用 override settings 装饰器 f
  • 使用 React Hook Form 和 Yup 进行文件输入验证

    我尝试使用 React Hook Form 进行文件输入验证 是的 我写了下面的代码 但是当我测试文件的大小时 它在这里显示console log value 0 size 即使我在文件输入中选择了一个文件 该值也是未定义的 这有什么问题吗
  • ES6 模板文字可以在运行时替换(或重用)吗?

    tl dr 是否可以制作可重用的模板文字 我一直在尝试使用模板文字 但我想我就是不明白 现在我感到沮丧 我的意思是 我想我明白了 但 它 不应该是它的运作方式 或者它应该如何实现 它应该变得不同 我看到的所有示例 甚至标记模板 都要求 替换
  • 原型链、构造函数、继承

    我正在玩 javascript 原型 我是新手 所以我有一个小问题 我正在用这个article http mckoss com jscript object htm作为指导 我已经定义了产品和书籍 目的是什么Book prototype c
  • Django admin.py 未知命令:'collectstatic'

    我已经从 django 1 2 7 升级到 django 1 5 1我正在使用 python 2 6 6当我尝试跑步时python manage py collectstatic i get 未知命令 collectstatic 从我的设置
  • django 南迁移,不设置默认值

    我使用 South 来迁移我的 Django 模型 然而 南方有一个令人讨厌的错误 它不会在 Postgres 数据库中设置默认值 例子 created at models DateTimeField default datetime no
  • 允许在 Safari 上聊天应用程序使用 audio.play()

    由于苹果禁用了自动播放音频的功能HTMLMedia Element play https developer mozilla org en US docs Web API HTMLMediaElement play在没有用户交互的 java
  • NodeJS 错误堆栈未定义 [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我正在使用节点检查器 我注意到new Error 有未定义的堆栈 如果我将此值分配给一个变量 该变量将显示堆栈未定义 有趣的是 跑步new
  • KML 中的 JavaScript 被 Google 地球插件忽略

    我创建了一个简单的 KML 文件 该文件可以在独立的 Google 地球客户端中运行 但在 Google 地球插件中根本无法运行 无论浏览器如何
  • React 路由器路由加载器不适用于嵌套组件

    我正在使用 React Router v6 我想在组件加载之前使用新的加载器来加载数据 所以我有以下内容 在我的index js中 const router createBrowserRouter createRoutesFromEleme
  • 如何始终将焦点保持在画布上?

    我一直在这个论坛寻找解决方案 但尚未找到 无论我在页面上的哪个位置单击 我都需要始终将焦点放在画布元素上 我有几个按钮 在每个 onclick 事件中我写 document getElementById canvas focus 这确实有效

随机推荐