Безопасность API и хранение секретов

API — это дверь к вашим данным, открытая программам. Её нужно и запирать, и не оставлять рядом ключи на виду.

Безопасность API — набор мер, гарантирующих, что к интерфейсу обращаются только те, кому можно, и только так, как задумано.

Базовые принципы защиты API

  • Аутентификация и авторизация. Проверяйте, кто обращается (токен/ключ) и имеет ли он право именно на это действие и именно над этими данными.
  • Валидация ввода. Любой параметр запроса — потенциально враждебный. Проверяйте типы, диапазоны, длину.
  • Только HTTPS. API без шифрования открывает токены и данные посредникам.
  • Минимум данных в ответе. Не отдавайте поля, которые клиенту не нужны (например, хэш пароля или внутренние id).

Контроль доступа к объектам

Частая ошибка — проверить, что пользователь вошёл, но не проверить, что запрашиваемый объект принадлежит именно ему. Если эндпоинт отдаёт заказ по номеру, а номер можно просто подставить чужой, любой залогиненный пользователь увидит чужие заказы. Всегда проверяйте принадлежность ресурса, а не только факт входа.

Ограничение частоты (rate limiting)

Лимит на число запросов с одного клиента за период защищает сразу от нескольких бед: перебора паролей, выкачивания данных, простого DDoS. Идея — считать запросы и отклонять лишние. Учебная иллюстрация принципа:

from collections import defaultdict

LIMIT = 3          # максимум запросов в окне
counts = defaultdict(int)

def handle(client):
    counts[client] += 1
    if counts[client] > LIMIT:
        return "429 Too Many Requests"
    return "200 OK"

for i in range(1, 6):
    print(f"Запрос {i}:", handle("user-1"))

Вывод:

Запрос 1: 200 OK
Запрос 2: 200 OK
Запрос 3: 200 OK
Запрос 4: 429 Too Many Requests
Запрос 5: 429 Too Many Requests

После исчерпания лимита запросы отклоняются — перебор и выкачивание становятся непрактичными.

Секреты: главный грех — закоммитить ключ

Секрет — любое значение, дающее доступ: пароль БД, API-ключ, приватный ключ, токен. Утечка одного секрета часто открывает всю систему.

Классическая ошибка — записать секрет прямо в код и закоммитить в репозиторий. Даже если потом удалить строку, секрет останется в истории git, а публичный репозиторий сканируют боты за минуты. Поэтому:

  • Секреты держат в переменных окружения или в специальном хранилище секретов, а не в коде.
  • Файлы с секретами (.env) добавляют в .gitignore, чтобы они не попали в репозиторий.
  • В репозитории оставляют только пример без значений, например .env.example.
  • Если секрет всё же утёк — его немедленно отзывают и меняют, а не «прячут».
# Плохо: ключ прямо в коде
API_KEY = "sk_live_1a2b3c..."   # утечёт вместе с репозиторием

# Хорошо: читаем из окружения, в код секрет не попадает
export API_KEY="sk_live_1a2b3c..."     # задаётся в окружении сервера
# в коде: api_key = os.environ["API_KEY"]

Ротация и срок жизни

Хорошая практика — периодически менять секреты (ротация) и давать токенам ограниченный срок. Тогда даже незамеченная утечка перестаёт работать со временем, а у злоумышленника окно меньше.

Итог

  • API защищают аутентификацией, авторизацией к конкретным объектам, валидацией ввода и HTTPS.
  • Rate limiting гасит перебор, выкачивание данных и часть DDoS.
  • Секреты держат в переменных окружения, а не в коде; .env — в .gitignore.
  • Утёкший секрет немедленно отзывают и меняют; помогает ротация и срок жизни.
Проверьте себя
1. Почему мало проверить только факт входа пользователя при выдаче объекта?
AВход проверять не нужно вовсе
BНужно ещё проверить, что объект принадлежит именно этому пользователю
CДостаточно проверить длину токена
DЭто замедляет API
2. От чего защищает rate limiting?
AОт XSS на странице
BОт перебора паролей, выкачивания данных и части DDoS
CОт устаревших сертификатов
DОт слабых паролей пользователей
3. Почему опасно закоммитить секрет в git, даже если потом удалить строку?
AGit не хранит удалённые строки
BСекрет остаётся в истории репозитория и его быстро находят боты
CЭто замедляет сборку
DНичего страшного в этом нет
4. Где правильно хранить секреты приложения?
AПрямо в коде для удобства
BВ переменных окружения или хранилище секретов, не в репозитории
CВ комментариях к коду
DВ URL запросов
Поддержать проект