$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогБазовая безопасность для API: авторизация, CORS, rate limiting и защита от повторов

Базовая безопасность для API: авторизация, CORS, rate limiting и защита от повторов

$ sudo teach IT
·23 июля 2026 г.·10 мин·58
Базовая безопасность для API: авторизация, CORS, rate limiting и защита от повторов

Соберём чек-лист практик для защиты веб-сервисов и разберём типовые уязвимости на уровне конфигурации и логики.

Содержание
1) Авторизация: от аутентификации к модели доступа1.1. Аутентификация ≠ авторизация1.2. Принцип “default deny” и точные проверки1.3. Bypass через “объектные” уязвимости (IDOR)1.4. Скоупы и “тонкий” JWT1.5. Ошибки статуса и сообщений2) CORS: когда “разрешить фронту” превращается в дыру2.1. Что реально делает CORS2.2. Главный анти-паттерн: с credentials2.3. Строгость по методам и заголовкам2.4. Preflight: атака через сложность2.5. Практический шаблон конфигурации3) Rate limiting: защита от перебора, злоупотребления и “тихого” DoS3.1. Что лимитировать и по каким ключам3.2. Границы, которые имеют смысл3.3. Алгоритм: fixed window, sliding window, token bucket3.4. Не забыть про разные статусы3.5. Пример middleware с Redis (идея)4) Защита от повторов: идемпотентность и replay-атаки4.1. Идемпотентность как базовая защита4.2. Что хранить: статус, результат, время4.3. Replay через старые токены4.4. Подводные камни при внедрении идемпотентности4.5. Пример логики идемпотентности на уровне БД5) “Базовый” чек-лист: что стоит проверить в первую очередь5.1. Авторизация и доступ5.2. CORS5.3. Rate limiting5.4. Повторы и идемпотентность6) Типовые сценарии атак и как на них реагировать6.1. “У меня есть токен — значит, можно всё”6.2. “Пускай CORS будет широким — так проще фронту”6.3. “Лимитов нет — будем посмотреть”

API редко ломают «хакерскими магиями». Чаще это последствия типовых решений на уровне конфигурации и бизнес-логики: неверно настроенная авторизация, слишком доверчивые CORS-политики, отсутствие ограничений частоты, а также повторяемые запросы без идемпотентности. В результате злоумышленник получает либо прямой доступ к данным, либо возможность нагрузить сервис, либо обойти целевые ограничения.

Ниже — системный чек-лист базовых практик безопасности для веб-сервисов и разбор самых распространённых уязвимостей, связанных с авторизацией, CORS, rate limiting и защитой от повторов. Цель — чтобы вы могли привести API к более устойчивому состоянию ещё до глубокого аудита.


1) Авторизация: от аутентификации к модели доступа

1.1. Аутентификация ≠ авторизация

Аутентификация отвечает на вопрос: «кто это?». Обычно это токен (JWT), сессия, API-ключ, mTLS и т.д.
Авторизация — «что он может?». Это проверка прав на ресурс и действие: RBAC/ABAC, scope/role, ACL.

Типичная ошибка: разработчики проверяют только наличие токена и считают, что «токен значит всё разрешено». На практике нужен слой проверок прав на конкретный эндпоинт и объект (tenant, user, project, order…).

1.2. Принцип “default deny” и точные проверки

Базовое правило: если вы не можете объяснить, почему запрос разрешён, — он должен быть запрещён.

Практика:

  • Проверяйте права до выполнения бизнес-логики.
  • Проверяйте права на конкретный ресурс, а не “в целом для пользователя”.
  • Делайте единый слой авторизации (policy layer), а не копируйте проверки по контроллерам.

Пример на псевдокоде:

code
// пример идеи: policy/check до выполнения операции
function can(user, action, resource) {
  // default deny
  if (!user) return false;

  // tenant isolation
  if (resource.tenantId !== user.tenantId) return false;

  // action checks
  if (action === 'read:order') return user.roles.includes('support') || user.roles.includes('admin');
  if (action === 'write:order') return user.roles.includes('admin');

  return false;
}

Важный нюанс: авторизацию нельзя воспринимать как статическую конфигурацию. Политики должны быть согласованы с бизнес-правилами.

1.3. Bypass через “объектные” уязвимости (IDOR)

Классика: клиент передаёт orderId, сервер извлекает объект по ID и не проверяет, что у пользователя есть доступ.

IDOR (Insecure Direct Object Reference) часто выглядит безобидно:

  • /api/orders/123 возвращает заказ 123
  • атакующий перебирает ID
  • получает чужие данные

Защита:

  • Проверка доступа после выборки объекта.
  • Или выборка только в рамках домена пользователя: WHERE order.tenantId = user.tenantId.

1.4. Скоупы и “тонкий” JWT

JWT полезен, но опасен при неправильной настройке:

  • Скоупы/claims могут быть слишком широкими («всё и сразу»).
  • Токен может быть использован для действий, для которых он не предназначен.
  • Сервер может полагаться на claim “role” без проверки подписи/актуальности.

Практический ориентир:

  • Используйте минимально достаточные claims.
  • На сервере проверяйте подпись токена (и алгоритм).
  • Согласуйте жизнь токена и необходимость отзыва (revocation).
  • Для критичных операций иногда нужна re-auth или server-side проверка актуального статуса пользователя.

1.5. Ошибки статуса и сообщений

Не выдавайте злоумышленнику подсказок:

  • Не различайте “нет токена” и “токен неверный” в сообщениях.
  • В логах фиксируйте детали, в ответах — нейтральные формулировки.
  • Следите за корректными HTTP-кодами:
    • 401 Unauthorized — нет/плохо аутентифицировали
    • 403 Forbidden — аутентификация есть, но прав недостаточно

2) CORS: когда “разрешить фронту” превращается в дыру

CORS — политика браузера. Она не защищает сервер напрямую, но существенно влияет на то, какие запросы сможет сделать вредоносный сайт из браузера жертвы.

2.1. Что реально делает CORS

Браузер при кросс-доменном запросе проверяет ответ сервера:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Credentials

Если политика слишком либеральная — любой сайт сможет получать ответы из вашего API при наличии у жертвы cookies/credentials.

2.2. Главный анти-паттерн: * с credentials

Нельзя делать одновременно:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Credentials: true

Браузер это заблокирует, но в некоторых реализациях возможны неожиданные эффекты. В целом безопасная стратегия — не использовать wildcard, если включены credentials.

Более правильный подход:

  • Валидация Origin и возврат конкретного домена из списка.
  • Либо отключать credentials, если они не нужны.

2.3. Строгость по методам и заголовкам

Частая ошибка — разрешить всё:

  • Access-Control-Allow-Methods: * (или список “на всякий случай”)
  • широкий Access-Control-Allow-Headers, включая чувствительные.

Что важно:

  • Разрешайте только те заголовки, которые реально нужны (например Authorization, Content-Type).
  • Ограничивайте методы (GET, POST, PUT, DELETE) по назначению.

2.4. Preflight: атака через сложность

Когда запрос не “простого типа”, браузер делает preflight OPTIONS. Если вы игнорируете безопасность на OPTIONS, но разрешаете слишком широко, вы открываете возможность для нежелательных запросов.

Чек-лист:

  • Обрабатывайте OPTIONS так же строго, как основной метод.
  • Применяйте авторизацию и ограничения (например rate limiting) и к preflight, если это критично.

2.5. Практический шаблон конфигурации

Пример Express (Node.js), который аккуратно валидирует Origin:

code
import express from "express";

const app = express();

const allowedOrigins = new Set([
  "https://app.example.com",
  "https://admin.example.com",
]);

app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (origin && allowedOrigins.has(origin)) {
    res.header("Access-Control-Allow-Origin", origin);
    res.header("Vary", "Origin");
    res.header("Access-Control-Allow-Credentials", "true");
    res.header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
    res.header("Access-Control-Allow-Headers", "Authorization,Content-Type");
  }
  // иначе: не добавляем CORS заголовки
  next();
});

app.options("*", (req, res) => res.sendStatus(204));

Ключевой момент: Vary: Origin — чтобы прокси/кэш не “подменяли” ответ между доменами.


3) Rate limiting: защита от перебора, злоупотребления и “тихого” DoS

Rate limiting — одна из самых эффективных мер в базовой безопасности. Она не отменяет уязвимость, но уменьшает ущерб и делает эксплуатацию медленной и дорогой.

3.1. Что лимитировать и по каким ключам

Нужно выбрать ось лимитирования. Типовые ключи:

  • по IP (X-Forwarded-For аккуратно, учитывая прокси)
  • по userId (после авторизации)
  • по API-key / token id
  • по комбинации (user + IP, или token + endpoint)

Ошибки:

  • Лимит только по IP: при NAT множество легитимных пользователей могут “толкаться”.
  • Лимит только по userId: злоумышленник может использовать множество аккаунтов (credential stuffing / account creation).

Практика: используйте разные лимиты для разных уровней:

  • общий лимит на IP/подсеть
  • отдельный лимит на userId или токен
  • отдельный лимит на “дорогие” эндпоинты (например поиск по графу, генерация отчётов)

3.2. Границы, которые имеют смысл

С точки зрения эксплуатации важно:

  • Учитывать специфичность: login/refresh — обычно требует жёстче лимитирования, чем public GET.
  • Установить лимиты по времени: per minute/per 5 minutes/per day.
  • Определить поведение при превышении: 429 Too Many Requests.

Пример (условный) политики:

  • /auth/login: 5 запросов / 10 минут / IP+username
  • /api/orders/{id}: 60 запросов / минуту / userId
  • /api/search: 30 запросов / минуту / userId+queryHash (если есть риск тяжелых запросов)

3.3. Алгоритм: fixed window, sliding window, token bucket

Классические подходы:

  • Fixed window: счётчик на фиксированном интервале (прост, но уязвим к “пилению по границе”)
  • Sliding window: точнее, но сложнее
  • Token bucket / leaky bucket: сглаживает всплески и удобен для QoS

Если вы используете готовые библиотеки — проверьте, какой именно алгоритм они применяют.

3.4. Не забыть про разные статусы

Rate limiting должен быть одинаково полезен:

  • Для 5xx иногда имеет смысл лимитировать иначе (или хотя бы логировать).
  • Для 4xx логика может различаться: частые 401/403 могут быть признаком атаки.
    Но слепо лимитировать любые ошибки опасно — можно заблокировать легитимных клиентов при ошибках конфигурации токенов.

3.5. Пример middleware с Redis (идея)

Псевдо-реализация (важно: реальный код зависит от выбранного пакета):

code
import rateLimit from "some-rate-limit-lib";

const limiter = rateLimit({
  store: "redis",
  points: 60,
  durationMs: 60 * 1000,
  keyGenerator: (req) => {
    const userId = req.user?.id;
    return userId ? `user:${userId}` : `ip:${req.ip}`;
  },
  onLimitExceeded: (req, res) => res.status(429).json({ error: "rate_limited" }),
});

app.use("/api", limiter);

Главное — не забывать, что лимит должен быть эффективен в распределенной среде (несколько инстансов). Поэтому обычно нужен единый store: Redis, Memcached, etc.


4) Защита от повторов: идемпотентность и replay-атаки

Повторы могут быть как случайными (ретраи клиента из-за тайм-аутов), так и злоумышленными (replay атаки на запросы, особенно если есть токены длительного действия и отсутствие защиты на уровне запросов).

4.1. Идемпотентность как базовая защита

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

Концепция: клиент отправляет ключ идемпотентности, сервер:

  • выполняет операцию один раз,
  • а при повторе того же ключа возвращает тот же результат, а не повторяет действие.

Пример: заголовок Idempotency-Key: <uuid>.

Серверная схема:

  1. Проверить наличие Idempotency-Key
  2. Привязать ключ к пользователю/ресурсу (или хотя бы userId)
  3. Хранить результат и/или статус операции
  4. При повторе вернуть прежний результат

4.2. Что хранить: статус, результат, время

Достаточно хранить:

  • статус (например processing/success/failed)
  • payload результата (или ссылку на запись)
  • timestamp и TTL (например 24 часа, 7 дней — по бизнес требованиям)

Нельзя бесконечно хранить: это превращается в утечку хранилища.

4.3. Replay через старые токены

Если у вас есть access token с длительным сроком, злоумышленник может перехватить запрос и воспроизвести его. Защита:

  • не только CORS/rate limiting
  • но и идемпотентность для опасных операций
  • короткое время жизни токенов
  • правильное использование nonce/challenge в сценариях критичных транзакций

Для API на уровне REST обычно достаточно idempotency keys и корректных проверок на стороне сервера.

4.4. Подводные камни при внедрении идемпотентности

  1. Ключ без привязки к пользователю
    Если ключ глобальный, кто-то может “подглянуть” ключ (или подобрать) и получить чужой результат. Поэтому идемпотентность должна быть в контексте пользователя/tenant.

  2. Ключ только для POST, но не для всего критичного
    Идемпотентность нужна не только POST. Всё зависит от бизнес-операций. Например, “подтвердить действие” может быть PUT/POST.

  3. Пересчёт результата при повторе
    Нужно возвращать тот же результат (или гарантировать эквивалентность). Иначе повтор превращается в конкурентное условие.

4.5. Пример логики идемпотентности на уровне БД

Упрощённая иллюстрация для SQL (идея):

code
-- таблица для идемпотентности
CREATE TABLE idempotency_keys (
  key_hash TEXT PRIMARY KEY,
  user_id UUID NOT NULL,
  route TEXT NOT NULL,
  request_body_hash TEXT NOT NULL,
  status TEXT NOT NULL,
  response_json JSONB,
  created_at TIMESTAMP NOT NULL DEFAULT now(),
  updated_at TIMESTAMP NOT NULL DEFAULT now()
);

В приложении:

  • считаете key_hash из Idempotency-Key
  • проверяете существование записи
  • если есть success — возвращаете response_json
  • если processing — можно вернуть 409/425/или ждать, зависит от UX
  • если нет — создаёте транзакцию/замок и выполняете операцию

Критично: делайте атомарность на уровне БД (уникальный индекс/транзакция), а не “проверил — сделал”.


5) “Базовый” чек-лист: что стоит проверить в первую очередь

Ниже — практический список, который обычно даёт максимальный эффект при минимальной цене исправлений.

5.1. Авторизация и доступ

  • есть модель доступа (RBAC/ABAC) и проверки “default deny”
  • нет IDOR: ресурс проверяется в контексте tenant/user
  • единый слой авторизации (или middleware), а не копипаста
  • правильные статусы: 401 vs 403
  • чувствительные детали ошибок не утекают в клиентские ответы

5.2. CORS

  • CORS включён только для доверенных Origin
  • нет * вместе с Access-Control-Allow-Credentials: true
  • корректные Vary: Origin
  • preflight OPTIONS обработан строго
  • разрешены только нужные методы и заголовки

5.3. Rate limiting

  • лимит на “публичные” и “опасные” эндпоинты различается
  • ключ лимитирования учитывает user/token после аутентификации
  • есть единый store (Redis) для распределённых инстансов
  • ответ при превышении — 429 и разумная политика заголовков (например Retry-After)
  • логирование и метрики (сколько 429, какие endpoints)

5.4. Повторы и идемпотентность

  • для операций с побочными эффектами (создание/оплата/отправка) есть idempotency key
  • ключ привязан к пользователю/tenant и по возможности к route
  • атомарность на уровне БД
  • хранится результат/статус и есть TTL
  • ретраи клиентов не приводят к двойным списаниям

6) Типовые сценарии атак и как на них реагировать

6.1. “У меня есть токен — значит, можно всё”

Симптом: эндпоинт возвращает чужие ресурсы при подстановке ID.
Причина: нет объектной проверки прав.
Фикс: проверка доступа к конкретному ресурсу или выборка с учётом tenant/user.

6.2. “Пускай CORS будет широким — так проще фронту”

Симптом: вредоносный сайт может делать запросы из браузера и читать ответы от API.
Причина: Access-Control-Allow-Origin возвращает слишком много вариантов.
Фикс: whitelist origin, ограничение заголовков, Vary: Origin.

6.3. “Лимитов нет — будем посмотреть”

Симптом: 10–100x рост нагрузки на конкретные эндпоинты, растущие latency и расходы.
Причина: отсутствует rate limiting или он слишком мягкий/не тем ключом.
Фикс: rate limits по endpoint и ключу (user/token + IP), отдельные лим

Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Solidity DLC - поймай, если сможешь

Solidity DLC - поймай, если сможешь

Аудит смарт-контрактов на реальных взломах. The DAO, Parity, Wormhole, Ronin, Euler и ещё 7 разборов: что пошло не так, сколько потеряли и что бы спасло. Атаки и защиты на Foundry, план входа в профессию аудитора.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Solidity pro - всё или ничего

Solidity pro - всё или ничего

Как пишут контракты, которые работают в основной сети. Наследование и OpenZeppelin, паттерны продакшена, оптимизация газа, ABI, delegatecall и подписи. 7 модулей, 33 урока, а в финале собственное dApp: мини-DAO с токеном, голосованием и казной.

Перейти к курсу

Другие статьи

Как выучить программирование самому и бесплатно: с чего начать и в каком порядке
выучить

Как выучить программирование самому и бесплатно: с чего начать и в каком порядке

Разберём понятный маршрут для новичка: как выбрать первый язык, что учить в первую очередь, какие мини-проекты делать и как не забросить занятия. Дам бесплатные ориентиры и план, чтобы знания складывались в портфолио.

21 сентября 2026 г.
1960
Как начать с HTML и сделать первую страницу: пошагово и без сложных слов
html

Как начать с HTML и сделать первую страницу: пошагово и без сложных слов

Разберём, что нужно новичку, чтобы за 1–2 вечера собрать свою первую веб-страницу: структура HTML, базовые теги, ссылки и картинки, плюс простой чек-лист, как понять, что страница работает правильно.

2 ноября 2026 г.
20
Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей
cargo

Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей

Разберём, как организовать Rust workspace, управлять optional features, фиксировать версии библиотек и уменьшать “дрейф” зависимостей при командной разработке и CI.

18 сентября 2026 г.
1960
Что такое HTML и чем он отличается от CSS — объясню простыми словами и на примерах
такое

Что такое HTML и чем он отличается от CSS — объясню простыми словами и на примерах

Разберём, из чего состоит веб-страница: что делает HTML (смысл и структура) и что делает CSS (оформление). В конце соберёте мини-страницу и поймёте, что нужно учить в каком порядке.

30 октября 2026 г.
130
Деплой без сюрпризов: как подготовить Docker-образ, переменные окружения и healthcheck
деплой

Деплой без сюрпризов: как подготовить Docker-образ, переменные окружения и healthcheck

Разберём практический чек-лист подготовки контейнера к продакшену: как собрать воспроизводимый образ, как правильно прокидывать конфиги/секреты через окружение и как настроить healthcheck, чтобы деплой не “успевал” раньше готовности сервиса.

18 сентября 2026 г.
1930
Что такое SQL и зачем он нужен новичку: как думать о данных простыми словами
такое

Что такое SQL и зачем он нужен новичку: как думать о данных простыми словами

Разберём, что такое базы данных и SQL без сложной теории: как выбирать данные, фильтровать их и собирать таблицы в отчёт. В конце — короткий план, как потренироваться и собрать первые запросы для портфолио.

28 октября 2026 г.
230

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

1) Авторизация: от аутентификации к модели доступа1.1. Аутентификация ≠ авторизация1.2. Принцип “default deny” и точные проверки1.3. Bypass через “объектные” уязвимости (IDOR)1.4. Скоупы и “тонкий” JWT1.5. Ошибки статуса и сообщений2) CORS: когда “разрешить фронту” превращается в дыру2.1. Что реально делает CORS2.2. Главный анти-паттерн: с credentials2.3. Строгость по методам и заголовкам2.4. Preflight: атака через сложность2.5. Практический шаблон конфигурации3) Rate limiting: защита от перебора, злоупотребления и “тихого” DoS3.1. Что лимитировать и по каким ключам3.2. Границы, которые имеют смысл3.3. Алгоритм: fixed window, sliding window, token bucket3.4. Не забыть про разные статусы3.5. Пример middleware с Redis (идея)4) Защита от повторов: идемпотентность и replay-атаки4.1. Идемпотентность как базовая защита4.2. Что хранить: статус, результат, время4.3. Replay через старые токены4.4. Подводные камни при внедрении идемпотентности4.5. Пример логики идемпотентности на уровне БД5) “Базовый” чек-лист: что стоит проверить в первую очередь5.1. Авторизация и доступ5.2. CORS5.3. Rate limiting5.4. Повторы и идемпотентность6) Типовые сценарии атак и как на них реагировать6.1. “У меня есть токен — значит, можно всё”6.2. “Пускай CORS будет широким — так проще фронту”6.3. “Лимитов нет — будем посмотреть”