← Все статьи

SiteGuard12 мин чтения

После перехода с оплаты пропадает сессия: как проверить SameSite и cookies

Если после оплаты пропадает сессия, сначала выясните, что произошло с cookie: исчезла из хранилища, осталась, но не ушла с запросом, или была отправлена, однако сервер не восстановил прежнее состояние. Эти случаи выглядят похоже: пустая корзина, повторный вход, сообщение «заказ не найден». Исправления у них разные.

Проверка SameSite и cookies после оплаты начинается с полной цепочки переходов и фактического запроса возврата. Одновременно отделите состояние браузерной сессии от статуса платежа: потеря первого не доказывает неуспех второго. Заказ должен быть связан с платёжной операцией на сервере, чтобы закрытая вкладка или недоступная cookie не разрушали учёт результата.

1. Разделите три разных сбоя

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

НаблюдениеГде искать причину
Исходной cookie нет в хранилищеУстановка, срок жизни, удаление, перезапись или другой профиль браузера
Cookie сохранена, но отсутствует в запросе возвратаSameSite, адрес, путь, HTTPS и ограничения браузера
Cookie отправлена, но открылась новая сессияОбработка заголовка и серверное состояние сессии

Не начинайте с очистки браузера: она уничтожит часть полезных свидетельств. Сначала сохраните безопасное описание исходного состояния, затем воспроизведите проблему в тестовом контуре. Значения сессионных cookies не публикуйте в задаче подрядчику; для сопоставления используйте обезличенные обозначения или подготовленные диагностические маркеры.

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

2. Запишите цепочку переходов целиком

Составьте короткую трассу: страница магазина, начало оплаты, страница провайдера, дополнительные подтверждения, первый запрос назад в магазин и конечная страница. Для каждого перехода запишите схему, хост, путь, метод и время. Важен именно первый запрос возврата: к моменту появления конечного экрана приложение уже могло выдать новую cookie.

Отдельно отметьте, где находится пользователь: на странице провайдера в основной вкладке или во встроенном платёжном блоке. Навигация всей вкладки и запрос из вложенного контекста подчиняются разным ограничениям. Схожий внешний вид формы оплаты не означает одинаковое поведение браузера.

Проверьте фактический метод возврата. В настройке может быть один Return URL, но провайдер способен открыть его через GET либо отправить на него POST. Дополнительные перенаправления усложняют картину. Читайте метод каждого запроса в журнале, а не определяйте его по адресу в строке браузера.

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

3. Снимите состояние до перехода и после него

Откройте DevTools до начала тестовой оплаты. В Application → Storage → Cookies выберите хост магазина. Запишите имя нужной cookie, Domain, Path, срок действия, Secure, HttpOnly и SameSite. Если Domain отсутствовал в исходном Set-Cookie, сохраните и этот факт: отображаемого имени домена в таблице недостаточно для понимания host-only режима.

В Network включите сохранение журнала при переходах и пройдите сценарий. Найдите первый запрос возврата: какие cookies браузер отправил, какие исключил и какое объяснение показывает инструмент. Затем проверьте Set-Cookie в ответе. Документация Chrome DevTools описывает таблицу cookies, просмотр их в Network и предупреждения о проблемах.

Сделайте три среза: перед уходом к провайдеру, в запросе возврата и после обработки его ответа. Это позволяет отличить неотправленную cookie от последующей перезаписи. Если видна только конечная гостевая сессия, причина уже скрыта за результатом работы приложения.

Отсутствие значения в document.cookie не является доказательством удаления. HttpOnly закрывает доступ из JavaScript, но подходящая cookie по-прежнему может отправляться в HTTP-запросах. Проверяйте сетевой заголовок и хранилище через инструменты браузера, а не снимайте HttpOnly ради диагностики.

4. Проверьте site, origin, хост и путь

SameSite использует понятие site, которое учитывает схему и регистрируемый домен. Origin дополнительно различает конкретный хост и порт. Например, https://shop.example и https://checkout.shop.example относятся к одному site, но имеют разные origins. Переход между HTTP и HTTPS меняет site, даже если имя хоста одинаковое. Эти правила рассматривает рабочий проект спецификации cookies HTTPWG.

При этом принадлежность одному site не расширяет область отдельной cookie. Host-only cookie, установленная на shop.example без Domain, не отправится на www.shop.example. Поэтому сравните адрес до оплаты и Return URL посимвольно. Незаметное добавление www способно объяснить проблему без изменения SameSite.

Далее проверьте Path. Cookie с Path=/checkout не подходит к запросу /payment/return. Параметры Domain и Path задают область отправки независимо от SameSite. Их смысл и допустимые значения описаны в справочнике Set-Cookie на MDN.

Исправление выбирайте по архитектуре: согласованный канонический хост, подходящий путь возврата и явно заданная область cookie. Расширение Domain на все поддомены меняет границу доверия. Его нельзя считать обязательным лечением любого сбоя: сначала определите, действительно ли другим хостам нужна эта сессия.

5. Сопоставьте SameSite с GET или POST

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

НастройкаВозврат GETВозврат POST
SameSite=StrictНе отправляется при cross-site возвратеНе отправляется при cross-site возврате
Явный SameSite=LaxДопускается для top-level навигации безопасным методомПослабление для такого перехода не действует
SameSite=None; SecureSameSite не запрещает отправкуSameSite не запрещает отправку

Строка Lax не означает разрешение для любого GET. Запрос изображения, программный запрос и переход внутри вложенного контекста не становятся навигацией основной вкладки только из-за метода. Аналогично None не означает, что cookie непременно уйдёт: остальные правила и политика браузера сохраняются.

При отсутствии явного SameSite некоторые браузеры применяют вариант Lax с временным послаблением: недавно созданная cookie может попасть и в cross-site POST, обычно в пределах двух минут. Это не правило для явно установленного Lax и не универсальная гарантия. Поэтому быстрый тест может пройти, а возврат после длительного подтверждения — сломаться. Зафиксируйте атрибут явно и проверьте задержанный сценарий.

6. Не считайте None универсальным исправлением

Запись SameSite=None требует Secure. Для рабочего сайта это означает корректное использование HTTPS. Однако cookie с атрибутами SameSite=None; Secure всё равно подчиняется остальным ограничениям браузера.

Руководство MDN по сторонним cookies описывает их блокировку и различия браузерных политик. Встроенный платёжный или авторизационный компонент может столкнуться с такими ограничениями даже при наличии None и Secure. Проверяйте целевые браузеры и режимы конфиденциальности; поведение одного профиля разработчика не описывает всю аудиторию.

Не смешивайте эту ситуацию с обычным возвратом всей вкладки на магазин. Здесь магазин снова становится верхней страницей, но первый переход всё равно нужно оценить по SameSite. При вложенном компоненте контекст другой. В отчёте укажите, какая именно cookie, на каком запросе и по какой причине недоступна.

Не переводите все cookies сайта в None и не снимайте CSRF-защиту. Если интеграции требуется особый режим, ограничьте изменение нужной cookie и проверенным сценарием. Для серверных уведомлений предусмотрите отдельную проверку подлинности. Изменение политики браузерной сессии не заменяет защиту операций, выполняемых от имени пользователя.

7. Исключите срок действия, HTTPS и одинаковые имена

Сравните Expires или Max-Age с длительностью всего сценария, включая задержку на стороне провайдера. Max-Age задаёт срок в секундах и имеет приоритет перед Expires, когда указаны оба атрибута. Отдельно проверьте срок серверной записи сессии: cookie может ещё существовать, хотя связанное состояние уже удалено.

Просмотрите каждый переход на наличие HTTP. Secure-cookie не предназначена для отправки по обычному незашифрованному соединению рабочего сайта. Исправляйте адрес возврата и конфигурацию HTTPS, а не снимайте Secure. Если схема определяется приложением через прокси, проверьте и то, какие атрибуты оно фактически выдаёт наружу.

Найдите все cookies с одинаковым именем. Например, старый вариант может иметь Path=/, а новый — Path=/payment. В подходящем запросе окажутся оба значения. В заголовке Cookie не передаются их Domain и Path, поэтому серверный разбор может выбрать не ту сессию. Рабочий проект HTTPWG отдельно предостерегает от зависимости от порядка одноимённых cookies.

Устранение дубликатов требует понимания, какой код устанавливает каждый вариант. Для удаления нужно адресовать правильную область, иначе старая запись останется. Также найдите ответ, который выдаёт новую сессию на возврате: иногда именно приложение перезаписывает ещё действующую cookie после того, как не получает её в первом запросе.

8. Учебный пример: cookie осталась, а сервер заменил сессию

Все адреса, идентификаторы и времена ниже условные. В тесте магазин https://shop.example в 12:00 выдаёт следующий заголовок. Значение demo_A — учебная метка, а не настоящий идентификатор:

Set-Cookie: shop_session=demo_A; Path=/; Max-Age=1800; Secure; HttpOnly; SameSite=Lax

При таком Max-Age срок составляет 30 минут от установки. Предположим, серверное состояние живёт достаточно долго, cookie не удаляется, другие ограничения не мешают. Пользователь уходит на отдельный site провайдера. Сравним две независимые пробы из одинакового начального состояния:

Проба в 12:01Первый запрос возвратаНаблюдение
Навигация основной вкладки через GETCookie содержит demo_AМагазин находит прежнюю сессию
Навигация основной вкладки через POSTCookie не содержит demo_AВ хранилище браузера demo_A ещё есть

Во второй пробе ошибочный обработчик считает отсутствие cookie признаком нового посетителя. Он отправляет Set-Cookie с demo_B под тем же именем и с той же областью. После ответа исходная запись заменяется. Наблюдатель, который смотрит только конечную страницу, ошибочно заключит, что платёжный сервис удалил сессию.

Повтор POST через пять минут тоже не получит явно заданную Lax-cookie при тех же условиях. Возраст одной минуты не давал ей специального разрешения. Если SameSite вообще не задан, сравнение быстрого и задержанного возврата дополнительно помогает обнаружить зависимость от временного браузерного послабления. Это основание исправить контракт интеграции, а не просить покупателей проходить оплату быстрее.

9. Если cookie отправлена, исследуйте сервер

Когда нужное значение присутствует в запросе, простая смена SameSite не объяснит потерю состояния. Сопоставьте запрос с обработкой на сервере. Найдена ли запись сессии, не истёк ли её срок, доступно ли общее хранилище всем экземплярам приложения, одинаковы ли параметры проверки и расшифровки?

Проверьте последовательность действий обработчика возврата. Он может сначала создать гостевую сессию, затем пытаться восстановить заказ уже из неё. Другой типичный участок — переключение между хостами или приложениями, которые одинаково называют cookie, но используют разные хранилища и ключи. Здесь причина находится в договоре между компонентами.

Логируйте безопасные диагностические признаки: идентификатор запроса, обслуживший экземпляр, найдено ли состояние, какой путь обработки выбран и был ли выдан новый Set-Cookie. Не сохраняйте полный сессионный секрет ради удобства. Для сопоставления старого и нового состояния предусмотрите отдельный безопасный маркер.

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

10. Подтверждайте оплату независимо от браузерной сессии

До ухода пользователя сохраните на сервере связь внутреннего заказа с идентификатором платёжной операции провайдера. Данные возврата используйте для поиска соответствующей операции, но не считайте их самостоятельным доказательством оплаты. Секреты, сессионные cookies и ключи API не передают через URL. Идентификатор заказа также не должен автоматически давать постороннему доступ к его деталям.

В официальном руководстве Stripe по обработке заказов показано, почему нельзя полагаться только на страницу возврата: покупатель может успешно заплатить и не открыть её. Руководство предусматривает серверную проверку состояния и повторно безопасную обработку. Это пример конкретной интеграции; для своего провайдера сверьте смысл его статусов и событий.

Сервер сопоставляет подтверждение с ожидаемым заказом, платёжным аккаунтом, суммой и валютой, сохранёнными при создании операции. Успешный статус чужого платежа не подходит. Посетитель способен открыть адрес результата без завершения оплаты, поэтому параметр вроде «успешно» в строке браузера не должен самостоятельно менять состояние заказа. Подлинность события и правильная привязка к заказу проверяются раздельно.

Webhook принимайте с проверкой подлинности по контракту сервиса. Например, документация Stripe требует проверки подписи с исходным телом запроса и описывает повторные доставки. Идемпотентность должна защищать результат заказа и при повторе уведомления, и при конкуренции уведомления с обработкой возврата. Одна операция не должна дважды запускать выдачу товара или услуги.

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

11. Исправьте подтверждённую причину и повторите контроль

Выбирайте минимальное обоснованное изменение. Для несовпадения хостов исправьте согласованный адрес возврата; для неверного Path — область нужной cookie; для отсутствующей серверной записи — хранение сессии. При cross-site POST изучите поддерживаемый провайдером маршрут и обработку заказа без обязательной пользовательской cookie. Не меняйте метод запроса произвольно, если это нарушает контракт интеграции.

Если подтверждена необходимость None, проверьте Secure, HTTPS и оставшиеся ограничения браузера. Если выбран возврат GET с Lax, проверьте именно навигацию основной вкладки и реальные перенаправления. Ни одна из этих настроек не заменяет серверную проверку статуса или права посетителя видеть заказ.

  • Повторите быстрый возврат и возврат после заметной задержки.
  • Проверьте предусмотренные GET и POST, не подменяя один другим в тесте.
  • Пройдите сценарий в поддерживаемых браузерах и соответствующем мобильном окружении.
  • Проверьте возврат раньше webhook и отсутствие возврата при поступившем webhook.
  • Повторите доставку уведомления и открытие страницы результата: бизнес-действие должно остаться единственным.

Испытайте как новую сессию, созданную после исправления, так и существовавшую до него. Изменение конфигурации не обязательно обновляет уже сохранённые cookies: проверьте фактический новый Set-Cookie и его область. Иначе разработчик увидит исправленный вариант, а часть посетителей продолжит использовать прежние атрибуты или столкнётся с одноимёнными записями.

Сравнивайте исходный и исправленный сценарии по тем же трём срезам cookies. Изменение в настройке считается проверенным, когда подтверждены и браузерное поведение, и правильное состояние заказа.

12. Зафиксируйте результат приёмки

В итоговой задаче должны остаться цепочка адресов и методов, атрибуты нужной cookie, причина её отсутствия или замены, исправленный компонент и результаты контрольных сценариев. Отдельно укажите, как заказ сопоставляется с подтверждённым платежом без браузерной сессии и как посетитель получает разрешённый доступ к результату.

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

Проверку cookies и тестовый платёжный сценарий выполняйте отдельно: доступность страницы не подтверждает сохранение сессии и правильность обработки оплаты.

Подключите SiteGuard для внешнего HTTP-контроля доступности важных страниц сайта.