Безопасность 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. - Утёкший секрет немедленно отзывают и меняют; помогает ротация и срок жизни.