Условие гонки для постоянных файлов cookie «Remember Me»

#authentication #cookies #login #race-condition

#файлы cookie #аутентификация #условие гонки

Вопрос:

Согласно рекомендациям по использованию файлов cookie для постоянного входа, вы никогда не должны разрешать использовать токен «запомнить меня» более одного раза:

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

Тогда как вы справляетесь с условием гонки, когда пользователь посещает несколько URL-адресов на вашем сайте одновременно?На самом деле у меня сейчас эта проблема.

Допустим, два запроса отправляются из браузера на сервер одновременно. Запросы не содержат сеансовых файлов cookie, но содержат тот же файл cookie «запомнить меня». Один из запросов будет обработан раньше другого и получит ответ с аутентифицированным файлом cookie сеанса и восстановленным файлом cookie «запомнить меня».

Токен «запомнить меня» во втором запросе теперь недействителен, и на сервере генерируется другой идентификатор сеанса. Этот запрос завершается ошибкой, поскольку пользователь не может быть аутентифицирован.

Я предложил несколько возможных решений, но ни одно из них не кажется очень хорошим. Я что-то упускаю?

Комментарии:

1. Я начинаю понимать, что проблема связана с тем, что http не имеет состояния. Если токен «запомнить меня» не был немедленно признан недействительным, ответы все равно возвращали бы разные сеансы (и браузер перезаписал бы старые сеансы последним).

Ответ №1:

Старый вопрос, но я нигде не нашел ответа. У меня была та же проблема. Моим решением было сохранить старый токен в базе данных и использовать его в качестве запасного варианта, если основной токен не найден. Но я убедился, что старый токен действителен только в течение короткого периода времени, например, через несколько секунд после изменения токена. Тогда я меняю токен, только если с момента предыдущего обновления прошло некоторое время, в противном случае будут случаи, когда токен меняется несколько раз подряд.

Ответ №2:

Чтобы подробнее остановиться на ответе вангоза, я бы добавил, что также необходимо использовать какой-то механизм блокировки. Рассмотрим этот псевдокод на PHP здесь:

 if (isFallback($token)) {
  // Log the user in
} else {
  // Usual processing
  // If token has to be updated, save old token as fallback for a few seconds
}
  

При наличии параллельных запросов они оба могут оказаться в ветке else, один запрос вызовет обновление, а другой приведет к аннулированию токена. Способ, которым я решил эту проблему, заключался в использовании именованной блокировки, названной в честь $token рассматриваемого, для переноса ветви else. Кроме того, все одновременные запросы, кроме одного, не смогут получить блокировку, и в этом случае мы немного подождем и повторим попытку (при повторной попытке мы обнаружим, что токен стал резервным токеном).

 if (isFallback($token)) {
  // Log the user in
} else {
  $couldLock = lock($token);
  if (!$couldLock) {
    usleep(10000);
    // Retry, possibly a recursive call
  } else {
    // Usual processing
    // If token has to be updated, save old token as fallback for a few seconds
    unlock($token);
  }
}
  

Я надеюсь, что эти соображения могут быть полезны.