Сеанс Laravel истекает случайно

У нас была эта проблема на нашем веб-сайте, что мы случайно получили ошибки CSRF от наших пользователей. У файлов cookie сеанса и данных сеанса было установлено, что срок действия истекает через 12 часов, а драйвер сеанса настроен на использование Redis. В дополнение к нашим исследованиям нам наконец удалось смоделировать условие исключения, так что вот сценарий:

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

У нас также были некоторые сообщения AJAX, которые были вызваны ошибками CSRF из-за этого условия.

Мне было интересно, сможет ли Laravel генерировать один и тот же идентификатор сеанса для обоих запросов безопасным образом.

Есть ли у кого-нибудь идеи, как мы можем исправить эту проблему?

P.S: мы используем laravel 4.1 с этой конфигурацией сеанса:

return array(

    'driver' => 'redis',

    'lifetime' => 720,

    'expire_on_close' => false,

    'files' => storage_path().'/sessions',

    'connection' => null,

    'table' => 'sessions',

    'lottery' => array(2, 100),

    'cookie' => 'laravel_session',

    'path' => '/',

    'domain' => '.ourdomain.com',
);

Ответ 1

После большого расследования проблемы в нашей компании, я пришел к такому результату, надеюсь, что это поможет, прежде всего, вот наши спецификации среды:

  • PHP: 5.3.3
  • LARAVEL: 4.1
  • ОС: centos 6 на сервере и os x mavericks в среде разработки
  • APACHE: 2
  • MYSQL: 5.6.19
  • REDIS: 2.4.10
  • PREDIS: 0.8. *

Прежде всего кажется, что исключение TokenMistmatch происходит в разных условиях, я почти изучил их все и смог решить некоторые из них, некоторые из них зависят от логики сеанса, а некоторые могут быть ошибками. В следующем я объясню каждую ситуацию, с которой я столкнулся.

1. Истекшие сеансы

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

2. Параллельные запросы, когда срок действия сеанса истек

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

3. Параллельные запросы при входе в систему

При входе в систему laravel по соображениям безопасности изменяет идентификатор сеанса, копирует данные сеанса и DESTROYS THE LAST SESSION, поэтому позвольте сказать по какой-то причине, когда происходит вход в параллельные запросы (несколько раз нажмите кнопку входа в систему), поэтому идентификатор сеанса будет регенерироваться несколько раз и DESTROYS последние сгенерированные сеансы на стороне сервера, так как некоторые из этих запросов по-прежнему используют предыдущий идентификатор сеанса (который больше не существует на стороне сервера), он приводит к регенерации токена CSRF, обратите внимание, что обычно laravel не регенерирует токен при входе в систему, если он может найти токен в гостевом сеансе (не вошел в систему), но в этом случае, когда гостевой сеанс будет уничтожен, в результате он будет регенерировать токен, и это может привести к исключению токена в других запросы, которые используют оригинальный токен. также обратите внимание, что не только эта проблема может привести к исключению токена, она также может привести к тому, что пользователь будет выведен из системы после одного запроса, поскольку одновременные запросы могут изменить сеанс сеанса. Я решил эту проблему, изменив одну строку в коде laravel, в Illuminate\Auth\Guard:updateSession:

 protected function updateSession($id)
 {
        $this->session->put($this->getName(), $id);

        //$this->session->migrate(true);
        $this->session->migrate()
 }

передача true методу переноса хранилища сеансов приведет к уничтожению сеанса на сервере после миграции, поэтому теперь данные останутся на сервере и будут уничтожены по истечении этого срока, а не по этому запросу, и это решит вопрос. Я не знаю, можем ли мы назвать это ошибкой в ​​laravel, но я думаю, что мы можем предложить лучшее решение для этого.

4. Браузеры

После изучения журналов выяснилось, что некоторые из этих токеновных исключений были из-за того, что браузер пользователей не принял cookie сеанса, я видел его больше всего на iOS Safari, я считаю, что мы ничего не можем с этим поделать.

5. Ошибка Redis

Некоторые из токенов-исключений на нашем сервере были вызваны redis, по какой-то причине laravel не смог прочитать данные сеанса с сервера redis при открытии сеанса, что приведет к регенерации токена CSRF. это происходило случайно на нашем сервере, поэтому я попытался изменить драйвер сеанса, и этот тип исключений исчез. Я пробовал базы данных, apc и драйверы файлов, и ни одна из них не вызвала эту проблему. Я еще не нашел, что вызывает ошибку, но я думаю, что это может быть ошибка с redis или predis library. (как вы знаете, laravel 4.1 не использует последнюю версию predis из-за проблем с совместимостью).

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

Ответ 2

Я сталкивался с этой проблемой раз в разы, без какой-либо конкретной причины, и это не упоминается Амиром.

Очистка файлов cookie домена работает для сценария, который я вижу.

Ответ 3

В моем случае это было проблемой с кэшированным файлом конфигурации. Laravel создает файл конфигурации, который кэшируется внутри папки bootstrap/cache. Переименуйте этот файл config.php на что-то еще и запустите "php artisan cache: clear"

Затем запустите сайт, и он должен работать нормально

Ответ 4

Это может быть связано с этой известной проблемой

Laravel 5.0 - Асинхронные запросы AJAX приводят к тому, что изменения переменных сеанса перезаписываются # 7549

Если несколько запросов пытаются перезаписать сеанс, а файлы сеанса не имеют блокировки, это может привести к повреждению (перезаписи) файла сеанса.