Безопасность API: чек-лист лучших практик для разработчика
Практика4 минРедакция Innovialabs
Коротко
Защищённый 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 Authorization | API отдаёт лишние поля или позволяет задать role или balance | Явные схемы ввода и ответа |
| API4 Unrestricted Resource Consumption | Нет лимитов на запросы, размер страницы, размер файлов | Rate limit, ограничение пагинации, таймауты |
| API5 Broken Function Level Authorization | Обычный пользователь вызывает эндпоинт администратора | Проверка роли на каждом эндпоинте, запрет по умолчанию |
| API6 Unrestricted Access to Sensitive Business Flows | Боты скупают товар или спамят регистрациями | Лимиты на уровне бизнес-логики и защита от ботов |
| API7 Server Side Request Forgery | API загружает 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, окружений и эндпоинтов; отключайте то, что больше не используется.