Сессия и безопасность
Где держать токен
Токен доступа — это право действовать от имени человека. Он не должен попадать в JavaScript: любой сторонний скрипт на странице, любое расширение браузера и любая XSS-уязвимость немедленно превращаются в кражу доступа.
Рабочая схема:
- код обменивает сервер, а не браузер;
- полученный токен сервер кладёт в куку с флагами
HttpOnly,SecureиSameSite=Lax; - браузер к API напрямую не ходит — запросы идут через ваш сервер, который подставляет токен.
Set-Cookie: sid=<токен>; Path=/; HttpOnly; SameSite=Lax; Secure; Max-Age=43200
Срок жизни сессии выбирает сервис. Удобный вариант — 12 часов со скользящим продлением: пока человек работает, сессия продлевается, а забытая на чужом компьютере закрывается сама.
Если интерфейсу нужно показать, что сессия скоро истечёт, кладите рядом вторую куку без HttpOnly —
с одной лишь отметкой времени. Токен при этом остаётся недоступным для скриптов.
Зачем state и PKCE
| Что | От чего защищает |
|---|---|
state | подмена возврата: без сверки злоумышленник может привести пользователя по своей ссылке с чужим кодом |
codeVerifier (PKCE) | перехват кода: код без исходной строки не обменивается на токен |
зарегистрированный redirectUri | угон кода на чужой адрес |
Все три проверки обязательны. state и codeVerifier держите в sessionStorage — они нужны ровно до
возврата и не должны жить дольше вкладки. После обмена удаляйте оба.
Требования к адресу возврата
- Адрес присылается в службу поддержки вместе с заявкой на идентификатор приложения. Незарегистрированный адрес не сработает.
- Боевой адрес — только
https. Для разработки запросите отдельный адрес и отдельный идентификатор приложения, чтобы боевой не пришлось отдавать на локальную машину. - Адрес в запросе авторизации и в обмене кода должен совпадать посимвольно, включая завершающий слэш.
- Не ведите возврат на страницу, которая сама по себе перенаправляет пользователя дальше по адресу из параметра: так открывается дыра в чужой сайт. Куда вернуть человека после входа, храните у себя — и принимайте только внутренние пути.
Ошибки при входе
| Что произошло | Что видно | Что делать |
|---|---|---|
redirectUri не зарегистрирован или не совпал | возврат не происходит, Инвойсбокс ID показывает ошибку | сверить адрес с тем, что зарегистрирован, вплоть до слэша |
state не совпал | ваша страница возврата не должна продолжать вход | начать вход заново; повторный переход по старой ссылке — нормальная причина |
| код уже использован | обмен отвечает ошибкой | код одноразовый: повторный обмен не делают, нужен новый вход |
| токен не принят | 200 с userId: null | считать, что входа нет: очистить куки и предложить войти снова |
| Инвойсбокс ID недоступен | обмен не завершился | показать человеку, что вход временно недоступен, и не создавать пустую сессию |