Как НЕ нужно делать авторизацию: разбор катастрофических ошибок

#security#authentication#webdev

TL;DR: Реальный кейс разработчика, который изобрел свою “авторизацию” с клиентской валидацией, хранением хэшей в localStorage и открытым доступом к админке через /a. Итог: $40k убытков и компрометация данных пользователей.

Введение

Безопасность веб-приложений — это не просто best practice, а обязательный минимум для любого разработчика. Однако, как показывает практика, даже опытные инженеры могут допускать катастрофические ошибки. Сегодня мы разберем реальный кейс, который наглядно демонстрирует, как НЕ нужно делать авторизацию и работать с пользовательскими данными.

Основная часть

1. Клиентская валидация как основной механизм безопасности

Один из самых вопиющих моментов — полное отсутствие серверной валидации. Вместо этого разработчик полагался исключительно на клиентскую логику. Например, успешный вход в систему определялся только по ответу сервера (201 статус), после чего пользователь перенаправлялся на админ-панель.

// Пример катастрофической реализации
fetch('/login', {
  method: 'POST',
  body: JSON.stringify({ username, password })
})
.then(response => {
  if (response.status === 201) {
    window.location.href = '/a'; // Админка открыта для всех
  }
});

Проблема: любой, кто знает URL админки (/a), мог получить доступ к защищенным ресурсам без авторизации.

2. Хранение чувствительных данных в localStorage

Логин пользователя и его данные сохранялись в localStorage, что само по себе является плохой практикой. Но хуже того — разработчик возвращал хэш пароля в ответе на успешный логин.

// Пример ужасной реализации
localStorage.setItem('user', JSON.stringify({
  id: 1,
  email: 'user@example.com',
  passwordHash: 'sha512hash' // Хэш пароля в localStorage
}));

Проблема: хэш пароля стал фактически паролем, так как сервер сравнивал именно его при авторизации.

3. Открытые эндпоинты и отсутствие безопасности

Эндпоинт /users возвращал список всех пользователей, включая их хэши паролей. Это не только нарушение конфиденциальности, но и прямая угроза безопасности.

# Пример запроса к открытому эндпоинту
GET /users

Проблема: любой мог получить доступ к данным всех пользователей.

4. Хэширование паролей на клиенте

Пароли хэшировались на клиенте, а затем сравнивались с хэшем на сервере. Это полностью нивелирует смысл хэширования, так как хэш становится фактическим паролем.

// Пример некорректного хэширования
const hashedPassword = sha512(password);
fetch('/login', {
  method: 'POST',
  body: JSON.stringify({ username, hashedPassword })
});

Проблема: если злоумышленник получит хэш, он может использовать его для входа в систему.

Практическое применение

Как правильно делать авторизацию

  1. Серверная валидация: Все проверки должны происходить на сервере. Клиентская валидация — это только UX улучшение.
  2. HTTPS: Все данные должны передаваться по защищенному соединению.
  3. JWT или сессии: Используйте токены или сессии для управления состоянием авторизации.
  4. Хэширование на сервере: Пароли должны хэшироваться только на сервере с использованием современных алгоритмов (например, bcrypt).
  5. Закрытые эндпоинты: Ограничивайте доступ к чувствительным данным с помощью middleware и ролей.

Пример правильной реализации:

// Серверная часть
app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  const user = await User.findOne({ username });
  if (!user || !await bcrypt.compare(password, user.passwordHash)) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  const token = jwt.sign({ userId: user.id }, process.env.SECRET_KEY);
  res.json({ token });
});

Заключение

Этот кейс — яркий пример того, как НЕ нужно подходить к разработке, особенно когда речь идет о безопасности. Даже если вы начинающий разработчик, всегда изучайте лучшие практики и не изобретайте велосипеды. Помните, что ошибки в безопасности могут привести к серьезным последствиям, включая финансовые потери и утрату доверия клиентов. Будьте профессионалами, и ваши проекты будут надежными и безопасными.


Источник: https://www.reddit.com/r/webdev/comments/1qrejoy/most_dumbest_thing_a_web_dev_has_ever_done/