Session Management: лучшие практики
Session Management — набор практик для безопасного создания, поддержания и завершения пользовательских сессий в веб-приложениях.
Зачем нужно
Слабое управление сессиями — одна из главных причин взломов (OWASP Top 10). Предсказуемые session ID, отсутствие инвалидации при logout, слишком длинный срок жизни — каждая из этих уязвимостей позволяет атакующему получить доступ к чужому аккаунту. Правильный session management снижает эту поверхность атаки.
Где используется
- Все приложения с серверными сессиями (Express, Django, Rails)
- Admin-панели с принудительным logout
- Банковские и финансовые приложения
- Приложения с управлением активными сессиями
Лучшие практики
1. Случайные и длинные session ID
// session ID должен быть непредсказуемым
const crypto = require('crypto');
const sessionId = crypto.randomBytes(32).toString('hex'); // 256-bit
// express-session делает это автоматически
// НЕ делайте так:
const badId = `session_${Date.now()}_${userId}`; // предсказуемо!
2. Регенерация ID после логина (session fixation)
app.post('/login', async (req, res) => {
// Проверяем credentials...
const user = await authenticate(req.body.email, req.body.password);
if (!user) return res.status(401).end();
// ОБЯЗАТЕЛЬНО регенерировать ID после логина
req.session.regenerate((err) => {
req.session.userId = user.id;
req.session.loginAt = Date.now();
req.session.ip = req.ip; // для последующей проверки
res.json({ ok: true });
});
});
3. Полное уничтожение сессии при logout
app.post('/logout', (req, res) => {
req.session.destroy(err => {
if (err) console.error(err);
// Удаляем куку на клиенте
res.clearCookie('connect.sid', { path: '/' });
res.json({ ok: true });
});
});
4. Ограничение срока жизни сессии
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 2 * 60 * 60 * 1000, // 2 часа активности
httpOnly: true,
secure: true,
sameSite: 'lax',
},
}));
// Absolute timeout — независимо от активности
function absoluteTimeout(req, res, next) {
const LOGIN_TIME = req.session.loginAt;
const MAX_SESSION = 8 * 60 * 60 * 1000; // 8 часов максимум
if (LOGIN_TIME && Date.now() - LOGIN_TIME > MAX_SESSION) {
return req.session.destroy(() => {
res.status(401).json({ error: 'Session expired' });
});
}
next;
}
5. Хранение в Redis (не в памяти)
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');
const client = createClient({ url: process.env.REDIS_URL });
client.connect;
app.use(session({
store: new RedisStore({ client, ttl: 7200 }), // TTL 2 часа
secret: process.env.SESSION_SECRET,
// ...
}));
6. Привязка сессии к IP / User-Agent
function validateSession(req, res, next) {
if (!req.session.userId) return res.status(401).end();
// Проверяем, что сессия не украдена
if (req.session.ip && req.session.ip !== req.ip) {
req.session.destroy(() => {});
return res.status(401).json({ error: 'Session hijacking detected' });
}
next;
}
Чеклист безопасной сессии
✅ session ID > 128 бит случайных данных
✅ regenerate после успешного логина
✅ destroy + clearCookie при logout
✅ HttpOnly + Secure + SameSite=Lax на куке
✅ Хранение в Redis, не в памяти процесса
✅ Absolute timeout (8 часов) + idle timeout (30 мин)
✅ HTTPS обязателен
Частые ошибки
MemoryStoreв продакшне — при рестарте все сессии теряются- Нет
regenerateпри логине — session fixation уязвимость - Logout только на клиенте (удаление куки без
destroyна сервере) — сессия живёт - Слишком долгий срок сессии (месяцы) для чувствительных приложений
Связанные темы
- _MOC Безопасность
- _MOC Сеть
- Cookie -- безопасные атрибуты (HttpOnly, Secure, SameSite)
- Сессии -- серверная авторизация
- Sessions и Cookies в Express
- JWT