При доступе к document.cook ie возвращается пустая строка, несмотря на то, что файлы cookie перечислены в инструментах разработчика, для флага httpOnly установлено значение false - PullRequest
0 голосов
/ 30 марта 2020

Иногда * при доступе к document.cookie на странице входа в систему я получаю пустую строку, даже если:

  1. куки перечислены в инструментах разработчика Chrome и Firefox,
  2. httpТолько флаг повара ie Мне нужно установить значение false,
  3. путь повара ie Мне нужно установить значение '/'.

Требуемое поведение

Одностраничное приложение My React (SPA) имеет страницу входа, которая содержит элемент <form /> для отправки учетных данных для входа в серверную часть. Когда ответ от бэкэнда получен и аутентификация прошла успешно, я проверяю, правильно ли был установлен повар аутентификации ie. В этом случае будет инициировано перенаправление, в котором будет отображаться контент для зарегистрированных пользователей.

Фактическое поведение

К сожалению, например, 15% попыток входа в систему, document.cookie возвращает пустую строку, которая предотвращает перенаправление и сохраняет пользователь на странице входа. Нажатие F5 не помогает, но при ручной замене пути URL после успешного запроса на вход в систему (например, обновление 'www.website.tld/ login ' до 'www.website.tld/ start ') пользователь перенаправляется на нужную страницу, предназначенную только для зарегистрированных пользователей.

Я не могу воспроизвести ошибку вручную. Кажется, это происходит случайно. Но когда это происходит и я заглядываю в консоль разработчика, я вижу все куки-файлы из бэкэнда (установлены правильно).

Дополнительная информация

  • django сервер работает в бэкэнде
  • желаемый повар ie устанавливается с response.set_cookie('key', 'value', secure=False httponly=False, samesite='strict')
  • JS libs (ax ios, response-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...

1 Ответ

0 голосов
/ 01 апреля 2020

У вас проблема с sameSite куки. См. Файлы cookie SameSite, объясненные , для объяснения:

Если вы установите для SameSite значение Строгий, ваш повар ie будет отправляться только в контексте первой стороны. [...] Когда пользователь находится на вашем сайте, повар ie будет отправлен с запросом, как и ожидалось. Однако при переходе по ссылке на ваш сайт, скажем, с другого сайта или по электронной почте от друга, по этому первоначальному запросу повар ie не будет отправлен. Это хорошо, если у вас есть файлы cookie, относящиеся к функциональность, которая всегда будет за начальной навигацией, такая как смена пароля или совершение покупки, но слишком ограничивающая для promo_shown. Если ваш читатель переходит по ссылке на сайт, он хочет, чтобы повар ie послал его, чтобы можно было применить их предпочтения.

Теперь у вас есть как минимум два варианта:

  • Рекомендуется: сохранить samesite=strict и изменить код вашего клиента. Веб-интерфейсу вообще не нужен доступ к файлам аутентификации, поэтому вы можете установить httponly=True. Затем введите внутренний API-интерфейс, который проверяет готовность ie на основе запросов от клиентского кода. Это дает вам дополнительное преимущество, заключающееся в том, что вы менее уязвимы для атак XSS, поскольку у кода веб-интерфейса нет доступа к auth cook ie.
  • Не рекомендуется: установите samesite в none или lax.
...