簡體   English   中英

即使 cookie 在 httpOnly 標志設置為 false 的開發人員工具中列出,訪問 document.cookie 也會返回空字符串

[英]Accessing document.cookie returns empty string even though cookies are listed in developer tools with httpOnly flag set to false

有時*,在登錄頁面訪問document.cookie ,我得到一個空字符串,即使:

  1. cookie 列在 Chrome 和 Firefox 開發人員工具中,
  2. 我感興趣的 cookie 的 httpOnly 標志設置為 false,
  3. 我感興趣的 cookie 路徑設置為“/”。

期望的行為

我的 React 單頁應用程序 (SPA) 有一個登錄頁面,其中包含一個<form />元素,用於將登錄憑據發送到后端。 當收到來自后端的響應並且身份驗證成功時,我檢查身份驗證 cookie 是否已正確設置。 如果是這種情況,將觸發重定向,顯示登錄用戶的內容。

實際行為

不幸的是,在大約15%的登錄嘗試中, document.cookie返回一個空字符串,這會阻止重定向並使用戶保持在登錄頁面上。 F5並不能解決問題,但是在成功登錄請求(例如,將“www.website.tld/ login ”更新為“www.website.tld/ start ”)后手動替換 url 路徑時,用戶將被轉發到僅用於登錄用戶的所需頁面。

我無法手動重現錯誤。 它似乎只是隨機發生。 但是當它發生時,我查看了開發人員控制台,我可以看到后端的所有 cookie(設置正確)。

附加信息

  • django 服務器正在后端運行
  • 使用response.set_cookie('key', 'value', secure=False httponly=False, samesite='strict')設置所需的 cookie
  • JS 庫(axios、react-router)

有關的:

登錄頁面 (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 cookie 的問題。 有關解釋,請參閱SameSite cookie說明:

如果您將 SameSite 設置為 Strict,則您的 cookie 將僅在第一方上下文中發送。 [...] 當用戶訪問您的網站時,cookie 將按預期與請求一起發送。 但是,當通過鏈接進入您的網站時,比如從另一個網站或通過朋友的電子郵件,在最初的請求中不會發送 cookie。 當您擁有與始終位於初始導航后面的功能相關的 cookie(例如更改密碼或進行購買)但對 promo_shown 的限制太大時,這很好。 如果您的讀者通過鏈接進入網站,他們希望發送 cookie,以便應用他們的偏好。

現在你至少有兩個選擇:

  • 推薦:保持samesite=strict並重構您的客戶端代碼。 前端根本不需要訪問 auth cookie,因此您可以設置httponly=True 然后引入一個后端 API,它根據來自客戶端代碼的請求驗證 cookie。 由於前端代碼無法訪問 auth cookie,這為您提供了不易受到 XSS 攻擊的額外優勢。
  • 不推薦:將samesite設置為nonelax

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM