Перейти к содержанию
Innovialabs

Безопасность API: чек-лист лучших практик для разработчика

Практика4 мин

Коротко

Защищённый API аутентифицирует каждый запрос, на сервере проверяет право пользователя на каждый конкретный объект и поле, валидирует ввод по строгим схемам, ограничивает объём запросов и логирует события безопасности.

Короткий ответ

Большинство серьёзных уязвимостей API вовсе не экзотические. API просто слишком доверяет клиенту: отдаёт объект, потому что ID корректный, сохраняет все поля из тела запроса или разрешает бесконечное число запросов. OWASP API Security Top 10 собирает эти шаблоны. Используйте чек-лист ниже при разработке и код-ревью.

OWASP API Top 10 в одной таблице

РискЧто идёт не такЧто делать
API1 Broken Object Level AuthorizationПользователь меняет /orders/123 на /orders/124 и видит чужой заказПроверять владельца каждого объекта на сервере
API2 Broken AuthenticationСлабые токены, нет защиты от перебора, токены без срока жизниПроверенные библиотеки, короткоживущие токены, лимиты на вход
API3 Broken Object Property Level AuthorizationAPI отдаёт лишние поля или позволяет задать role или balanceЯвные схемы ввода и ответа
API4 Unrestricted Resource ConsumptionНет лимитов на запросы, размер страницы, размер файловRate limit, ограничение пагинации, таймауты
API5 Broken Function Level AuthorizationОбычный пользователь вызывает эндпоинт администратораПроверка роли на каждом эндпоинте, запрет по умолчанию
API6 Unrestricted Access to Sensitive Business FlowsБоты скупают товар или спамят регистрациямиЛимиты на уровне бизнес-логики и защита от ботов
API7 Server Side Request ForgeryAPI загружает URL от пользователя и достукивается до внутренних сервисовБелый список адресов, блокировка внутренних сетей
API8 Security MisconfigurationПодробные ошибки, открытый CORS, нет TLSБезопасные настройки по умолчанию, ревью конфигов
API9 Improper Inventory ManagementСтарый /v1 или тестовые эндпоинты всё ещё работаютВести реестр, отключать старые версии
API10 Unsafe Consumption of APIsСлепое доверие данным из сторонних APIВалидировать внешние данные как пользовательский ввод

Аутентификация

  • Каждый эндпоинт требует аутентификации, если он явно не объявлен публичным.
  • Используйте стандартные механизмы (OAuth 2.0 / OpenID Connect, сессии, правильно настроенный JWT) и поддерживаемые библиотеки — не изобретайте свой формат токенов.
  • Access-токены живут недолго; обновление и отзыв продуманы.
  • На эндпоинтах входа, сброса пароля и OTP — строгие лимиты.
  • API-ключи для интеграций между серверами ограничены по правам, регулярно меняются и никогда не попадают в код мобильного приложения или фронтенда.

Авторизация на уровне объектов

Это самая частая критическая ошибка в API. Правило: никогда не загружайте объект только по ID — всегда учитывайте, кто спрашивает.

app.get("/api/orders/:id", requireAuth, async (req, res) => {
  const order = await db.order.findFirst({
    where: { id: req.params.id, userId: req.user.id },
  });
  if (!order) return res.status(404).json({ error: "Not found" });
  res.json(order);
});

Случайные UUID вместо последовательных ID усложняют подбор, но не заменяют эту проверку.

Валидация и mass assignment

  • Проверяйте тело запроса, query-параметры и заголовки по строгой схеме: типы, длины, форматы, допустимые значения.
  • Отклоняйте неизвестные поля, а не сохраняйте их молча. Иначе пользователь отправит "role": "admin" вместе с обновлением профиля.
  • Переносите данные в базу явно; не передавайте req.body напрямую в update ORM.
  • Схема ответа перечисляет, какие именно поля уходят наружу, — не сериализуйте запись из базы целиком.
const UpdateProfile = z.object({
  name: z.string().min(1).max(100),
  phone: z.string().max(20).optional(),
}).strict();

const data = UpdateProfile.parse(req.body);

Лимиты запросов и ресурсов

  • Лимиты на пользователя, API-ключ и IP, более строгие — для дорогих и чувствительных эндпоинтов.
  • Максимальный размер страницы, тела запроса, загружаемого файла и сложности запроса (для GraphQL — ограничения глубины и стоимости).
  • Таймауты на запросы к базе и исходящие запросы.
  • Возвращайте 429 Too Many Requests, чтобы добросовестные клиенты могли притормозить.

Сообщения об ошибках

  • Клиент получает короткое сообщение и ID ошибки, подробности уходят в логи.
  • В ответах нет stack trace, кусков SQL и внутренних имён хостов.
  • При входе и сбросе пароля не раскрывайте, существует ли такой email.
  • Отвечайте одинаково на «не найдено» и «не ваше», чтобы не подтверждать существование объекта.

CORS

  • Разрешайте только те origin, которым действительно нужен доступ из браузера, а не * для API с авторизацией.
  • Никогда не отражайте любой входящий Origin вместе с Access-Control-Allow-Credentials: true.
  • Помните: CORS управляет только браузерами. От скриптов, серверов и curl он API не защищает.

Логи и реестр API

  • Логируйте неудачные входы, отказы в доступе, ошибки валидации и срабатывания лимитов с ID пользователя и ID запроса.
  • Никогда не пишите в логи пароли, токены и полные персональные данные.
  • Настройте оповещения на необычные паттерны, например когда один пользователь перебирает тысячи разных ID объектов.
  • Ведите актуальный список версий API, окружений и эндпоинтов; отключайте то, что больше не используется.

Частые вопросы

Для интеграции сервер–сервер с доверенным партнёром — может быть, если ключ ограничен по правам, регулярно меняется и передаётся только по HTTPS. Для конечных пользователей ключ, зашитый в приложение, не секрет — нужна аутентификация пользователя.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.