Код-ревью Middle Аутентификация и сессии обновлено

Ревью: вход и сброс пароля на Node.js

Вопрос

Найдите проблемы аутентификации в коде.

app.post('/login', async (req, res) => {
  const user = await db.users.findOne({ email: req.body.email });
  if (!user) return res.status(404).json({ error: 'Пользователь не найден' });
  if (user.password !== md5(req.body.password)) return res.status(401).json({ error: 'Неверный пароль' });
  req.session.userId = user.id;
  res.json({ ok: true });
});

app.post('/forgot', async (req, res) => {
  const token = Math.random().toString(36).slice(2);
  await db.resets.insert({ email: req.body.email, token });
  await mail(req.body.email, `https://${req.headers.host}/reset?token=${token}`);
  res.json({ ok: true });
});

Коротко

Почти все классические ошибки входа в 14 строках. Исправления: одинаковый ответ, медленный хеш, перевыпуск сессии, лимиты, криптографический токен с хешем, сроком и одноразовостью, базовый URL из конфигурации.

Что должен найти ревьюер

  1. Безопасностьстрока 3
    Разные ответы и коды для «нет пользователя» и «неверный пароль»: перечисление аккаунтов.
  2. Безопасностьстрока 4
    MD5 без соли для паролей: перебирается мгновенно. Нужен Argon2id или bcrypt. Сравнение через !== тоже не за постоянное время.
  3. Безопасностьстрока 5
    Идентификатор сессии не перевыпускается при входе (req.session.regenerate): фиксация сессии.
  4. Безопасностьстрока 1
    Нет ограничения попыток входа: перебор и credential stuffing.
  5. Безопасностьстрока 10
    Math.random не криптографический генератор: токен сброса предсказуем. Нужен crypto.randomBytes(32).
  6. Безопасностьстрока 11
    Токен хранится в открытом виде и без срока жизни и признака использования.
  7. Безопасностьстрока 12
    Ссылка строится из заголовка Host: отравление ссылки сброса.

Разбор

Почти все классические ошибки входа в 14 строках. Исправления: одинаковый ответ, медленный хеш, перевыпуск сессии, лимиты, криптографический токен с хешем, сроком и одноразовостью, базовый URL из конфигурации.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.