$ 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 мин·44
Базовая безопасность для 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 – для начинающих!

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

Перейти к курсу
Простыми словами о защищенном соединении

Простыми словами о защищенном соединении

Базовый курс для обычных людей: что такое защищённое соединение, как оно устроено, что оно умеет и чего не умеет, как выбрать сервис и настроить его на своих устройствах.

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

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

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

Перейти к курсу
Сервис, который не падает - как построить безопасную зашифрованную сеть

Сервис, который не падает - как построить безопасную зашифрованную сеть

Практический курс по безопасности сети: как собрать собственный сервис защищённого доступа - от пустого сервера до подписки с автовыбором - и, что важнее, как потом не уронить его своими руками.

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

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

Создание Telegram бота в 2026 легко и просто! Полный курсы!
создание

Создание Telegram бота в 2026 легко и просто! Полный курсы!

19 июня 2026 г.
1431
Как выбрать, куда идти в IT: веб, боты, данные — без хаоса
выбрать

Как выбрать, куда идти в IT: веб, боты, данные — без хаоса

Разберём самые понятные направления для новичка (веб, боты, аналитика/данные) и чем они отличаются по шагам обучения. В конце вы получите простой план, как проверить, что вам подходит, и какой стартовый путь выбрать.

5 октября 2026 г.
580
Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть
путь

Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть

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

17 сентября 2026 г.
1070
Как понять, какой язык программирования учить первым: простой тест и выбор
понять

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

Помогу новичку выбрать первый язык без стресса: объясню варианты по целям (веб, боты, анализ данных), что учить в первую очередь и как не потратить месяцы впустую. В конце — краткий план старта на ближайшие недели.

2 октября 2026 г.
630
Запуск и поддержка проекта на Ruff в команде: формат “как в CI”, а не “как договорились”
запуск

Запуск и поддержка проекта на Ruff в команде: формат “как в CI”, а не “как договорились”

Как правильно выстроить конфигурацию Ruff (линтинг, форматирование, автозамены) и сделать так, чтобы одинаковые правила гарантировались в локальной сборке и на CI без ручного шаманства.

16 сентября 2026 г.
1050
Что такое переменные, типы и условия в коде: объясню без матана на примерах
такое

Что такое переменные, типы и условия в коде: объясню без матана на примерах

Разберём ключевые понятия для старта — переменные, типы данных, сравнения и условные операторы — простыми словами и на бытовых примерах. В конце соберём мини-скрипт, который принимает решение по введённым данным.

30 сентября 2026 г.
660

Комментарии

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

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

Содержание

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. “Лимитов нет — будем посмотреть”