Сессии: серверная авторизация
Серверные сессии — механизм авторизации, при котором сервер хранит состояние пользователя (сессию), а клиент идентифицирует себя через session ID в куке.
Зачем нужно
Сессии — классический подход к авторизации до эпохи JWT. Сервер полностью контролирует жизненный цикл сессии: может немедленно инвалидировать её (logout, блокировка). JWT этого не умеет без дополнительной инфраструктуры. Понимание сессий необходимо для работы с Express.js, Django, Rails.
Где используется
- Традиционные веб-приложения с серверным рендерингом (MPA)
- Admin-панели с возможностью принудительного logout
- Приложения с высокими требованиями безопасности (банки)
- Express.js с
express-session
Принцип работы
1. Пользователь отправляет login + password
2. Сервер проверяет credentials
3. Сервер создаёт сессию: { sessionId: "abc123", userId: 42, createdAt: ... }
4. Сессия сохраняется в хранилище (Redis, БД, память)
5. Сервер возвращает куку: Set-Cookie: sessionId=abc123; HttpOnly; Secure
6. При каждом запросе браузер автоматически отправляет куку
7. Сервер ищет сессию по sessionId и знает, кто делает запрос
Реализация в Express
const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');
const app = express;
const redisClient = createClient({ url: process.env.REDIS_URL });
redisClient.connect;
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET, // длинная случайная строка
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // недоступно через JS
secure: true, // только HTTPS
sameSite: 'lax', // защита от CSRF
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 дней
},
}));
// Логин — создаём сессию
app.post('/auth/login', async (req, res) => {
const { email, password } = req.body;
const user = await db.users.findByEmail(email);
if (!user || !await bcrypt.compare(password, user.passwordHash)) {
return res.status(401).json({ error: 'Неверные данные' });
}
// Регенерация ID сессии — защита от session fixation
req.session.regenerate(err => {
if (err) return res.status(500).json({ error: 'Session error' });
req.session.userId = user.id;
req.session.role = user.role;
res.json({ ok: true });
});
});
// Middleware авторизации
function requireAuth(req, res, next) {
if (!req.session.userId) {
return res.status(401).json({ error: 'Не авторизован' });
}
next;
}
// Защищённый маршрут
app.get('/api/profile', requireAuth, async (req, res) => {
const user = await db.users.find(req.session.userId);
res.json(user);
});
// Logout — уничтожаем сессию
app.post('/auth/logout', (req, res) => {
req.session.destroy(err => {
res.clearCookie('connect.sid');
res.json({ ok: true });
});
});
Сессии vs JWT
| Критерий | Сессии | JWT |
|---|---|---|
| Хранение | Сервер (Redis/DB) | Клиент (localStorage/cookie) |
| Инвалидация | Немедленная | Ждать истечения токена |
| Масштабирование | Нужен Redis | Без хранилища |
| Размер | Малый (только ID) | Больше (весь payload) |
| Состояние | Stateful | Stateless |
Частые ошибки
- Хранение сессий в памяти процесса (
MemoryStore) — при перезапуске теряются все сессии - Не регенерируют session ID после логина — уязвимость session fixation
- Cookie без
HttpOnly— доступно черезdocument.cookie(XSS-уязвимость) - Cookie без
Secure— передаётся по HTTP (перехват) - Не очищают сессию при logout — сессия остаётся валидной в хранилище
Связанные темы
- _MOC Сеть
- _MOC Безопасность
- Cookie -- как работают
- Cookie -- безопасные атрибуты (HttpOnly, Secure, SameSite)
- Sessions и Cookies в Express
- JWT