Материалы / 14 минут
Разделить постоянные данные, временные сессии и правила доступа без хранения секретов в браузере.
Каждое хранилище решает свою задачу
PostgreSQL хранит пользователей, продуктовые сущности и историю, которую нельзя потерять после истечения TTL. Redis хранит короткоживущую серверную сессию и быстро проверяет её на каждом запросе.
Браузер получает только случайный непрозрачный идентификатор. Email, роль и другие данные сессии не должны быть закодированы в доступном JavaScript токене.
Сессия — это жизненный цикл
При входе создаётся новый идентификатор, старые токены не переиспользуются. Cookie получает HttpOnly, Secure и подходящий SameSite. В Redis задаётся TTL, а выход удаляет серверную запись.
Для выхода со всех устройств храните индекс активных сессий пользователя. Смена пароля и удаление аккаунта должны инвалидировать весь индекс.
- Сравнивайте пароль только через адаптивный password hash.
- Не сообщайте, существует ли email, в восстановлении доступа.
- Защищайте изменяющие запросы от CSRF и проверяйте Origin.
Схема меняется миграциями
Миграция должна быть повторяемой, ограниченной одной задачей и применяться до запуска версии, которая зависит от новой схемы.
Индексы добавляются под измеренный запрос. Уникальные ограничения и внешние ключи фиксируют инварианты ближе к данным, а не только в handler.
CREATE TABLE users (
id uuid PRIMARY KEY,
email text NOT NULL UNIQUE,
password_hash text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
-- Redis: session:<random-id> -> user id + expiry