Ты написал API, всё работает, тесты проходят, фронт дёргает эндпоинты — красота. А потом кто-то сливает токен в публичный репозиторий, бот начинает долбить твой сервер 10 000 запросами в секунду, а стажёр через API случайно удаляет продакшн-базу.
Добро пожаловать в мир API-безопасности. Давай разберём три главных столпа, на которых всё держится, — и где разработчики спотыкаются чаще всего.

🎫 1. Токены: твой цифровой пропуск
Что это и зачем?
Токен — это строка, которая говорит серверу: «Я — тот, за кого себя выдаю». Без токена любой запрос к защищённому API получит в ответ холодное 401 Unauthorized.
Самые популярные схемы:
API Key — простой ключ в заголовке или query-параметре. Удобно, но грубо.
JWT (JSON Web Token) — самодостаточный токен с payload, подписью и сроком жизни.
OAuth 2.0 Access Token — выдаётся через сервер авторизации, идеально для сторонних приложений.
Где косячат?
❌ Хранение токенов в коде
Классика жанра. Разработчик пишет const API_KEY = "sk-live-abc123..." прямо в исходниках. Пушит в GitHub. Бот находит ключ за 30 секунд. Привет, утечка.
// ❌ Так делают только враги самим себеconst API_KEY = "sk-live-abc123def456";
// ✅ Переменные окружения — наше всёconst API_KEY = process.env.API_KEY;❌ Токены без срока жизни
Выдал токен — и он живёт вечно. Если его украдут, злоумышленник получает бессрочный доступ. JWT без exp — это как ключ от квартиры, который невозможно перевыпустить.
❌ Передача токенов через URL
Когда токен летит в query-параметре (?token=abc123), он оседает в логах сервера, в истории браузера, в аналитике. Всегда используй заголовок Authorization:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
❌ Отсутствие ротации
Один и тот же ключ используется годами. Правило простое: регулярно меняй ключи и используй пару access/refresh токенов.
🚦 2. Rate Limiting: тормоза для API
Что это и зачем?
Rate limit — это ограничение на количество запросов за единицу времени. Без него один агрессивный клиент (или бот) может положить весь сервис.
Где косячат?
❌ Rate limit вообще не настроен
Удивительно, но многие API выходят в прод без каких-либо ограничений. Пока трафик маленький — проблем нет. Но первая же DDoS-атака или неаккуратный скрипт с бесконечным циклом — и сервер ложится.
❌ Лимит только по IP
Звучит логично, но за одним IP может скрываться целый офис или VPN. А злоумышленник может распределить запросы по тысячам адресов. Лучше лимитировать по токену/пользователю:
# Пример с Redis на Pythondef check_rate_limit(user_id):
key = f"rate:{user_id}"
current = redis.incr(key)
if current == 1:
redis.expire(key, 60) # окно — 60 секунд
if current > 100:
raise RateLimitExceeded()❌ Нет информативных ответов
Когда клиент упирается в лимит, он должен понимать, что произошло. Отдавай 429 Too Many Requests и полезные заголовки:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700000000
❌ Одинаковые лимиты для всех эндпоинтов
Запрос GET /users и POST /payments — это совершенно разные операции по нагрузке и критичности. Тяжёлые и чувствительные операции (оплата, отправка писем, генерация отчётов) должны иметь более строгие лимиты.

👑 3. Права доступа: не все равны
Что это и зачем?
Даже если пользователь аутентифицирован (мы знаем, кто он), это не значит, что ему можно всё. Авторизация отвечает на вопрос «что именно тебе разрешено?».
Где косячат?
❌ Проверка прав только на фронтенде
Кнопка «Удалить» скрыта в UI для обычного пользователя? Отлично. Но если он напрямую отправит DELETE /api/users/42 через curl — сервер выполнит запрос. Проверка прав должна быть на бэкенде. Всегда.
// ❌ Фронтенд «защита»
{role === 'admin' && <button onClick={deleteUser}>Удалить</button>}
// ✅ Бэкенд проверка — обязательна
app.delete('/api/users/:id', (req, res) => {
if (req.user.role !== 'admin') {
return res.status(403).json({ error: 'Forbidden' });
}
// ... удаление
});❌ IDOR — Insecure Direct Object Reference
Одна из самых частых уязвимостей. Пользователь запрашивает GET /api/orders/123 и видит свой заказ. Меняет 123 на 124 — и видит чужой. Сервер не проверяет, принадлежит ли ресурс текущему пользователю:
// ❌ Доверяем ID из URL без проверки владельца
app.get('/api/orders/:id', async (req, res) => {
const order = await Order.findById(req.params.id);
res.json(order); // чей угодно заказ
});
// ✅ Фильтруем по владельцу
app.get('/api/orders/:id', async (req, res) => {
const order = await Order.findOne({
_id: req.params.id,
userId: req.user.id // только свои заказы
});
if (!order) return res.status(404).json({ error: 'Not found' });
res.json(order);
});❌ Слишком широкие scope у токенов
OAuth позволяет выдавать токены с ограниченными правами (scopes). Но разработчики часто запрашивают scope: * — полный доступ ко всему. Если такой токен утечёт, последствия будут максимальными. Принцип минимальных привилегий: давай ровно столько прав, сколько нужно для конкретной задачи.
❌ Нет разделения ролей
Два типа пользователей: «авторизован» и «не авторизован». А нужно: admin, manager, user, readonly, service. Грамотная ролевая модель — это не over-engineering, а необходимость.
🚀 Хочешь разобраться глубже?
Если ты только начинаешь свой путь в программировании или хочешь подтянуть практические навыки — попробуй Кодик. Это приложение, в котором ты изучаешь программирование через практику: не просто читаешь теорию, а сразу пишешь код и решаешь задачи.
А ещё заходи в наше сообщество в Telegram — там регулярно выходят полезные посты про разработку, разборы задач и свежие материалы. Нас уже больше 2000 разработчиков!
Не будь тем разработчиком, чей API попадает в новости. Проверь свой чек-лист сегодня. 🔒
