Перейти к содержанию
Innovialabs

Безопасность Telegram-бота: токены, вебхуки и контроль доступа

Продвинутое4 мин

Коротко

Храните токен как пароль, проверяйте каждый запрос вебхука по секретному заголовку, сверяйте права по user_id на сервере и считайте любой ввод, включая callback data, недоверенным.

Коротко

Telegram-бот — это публичная точка входа, к которой может обратиться кто угодно. Большинство инцидентов происходит в пяти местах:

  • Токен утекает, и ботом управляет кто-то другой.
  • Вебхук принимает запросы, которые пришли не от Telegram.
  • Админские команды защищены username или скрытой кнопкой вместо настоящей проверки.
  • Ввод и callback data считаются надёжными, будто их сгенерировал сам бот.
  • Данные пользователей хранятся дольше и шире, чем нужно.

Закройте эти пять пунктов — и бот будет защищён лучше большинства.

Токен: хранение, утечки и отзыв

Токен даёт полный контроль над ботом: чтение апдейтов, отправку сообщений, смену вебхука. Обращайтесь с ним как с паролем от базы данных.

  • Держите его в переменных окружения или менеджере секретов, а не в репозитории. Добавьте .env в .gitignore и включите сканирование секретов в Git-хостинге.
  • Следите за логами: URL Bot API содержат токен (/bot<token>/sendMessage). Его могут сохранить debug-логи HTTP-клиента, трекеры ошибок и логи прокси.
  • Используйте разных ботов для разработки и продакшена, чтобы тестовый токен на ноутбуке не имел доступа к реальным пользователям.

Если токен утёк, отзовите его в @BotFather (команда /revoke или настройки бота). Старый токен сразу перестаёт работать. Затем выкатите новый токен, заново установите вебхук с новым секретом и проверьте, что бот отправлял, пока токен был скомпрометирован.

Вебхук: убедитесь, что запрос от Telegram

URL вебхука — обычный HTTPS-эндпоинт. Если его угадали или он утёк, кто угодно может прислать поддельный апдейт, например «оплату» или сообщение от «админа».

При вызове setWebhook передайте secret_token. Telegram будет добавлять его в каждый запрос в заголовке X-Telegram-Bot-Api-Secret-Token, а ваш сервер отклоняет всё, где значение не совпадает.

import hmac

def is_from_telegram(headers: dict, expected: str) -> bool:
    received = headers.get("X-Telegram-Bot-Api-Secret-Token", "")
    return hmac.compare_digest(received, expected)

Что ещё стоит сделать:

  • Используйте неочевидный путь вместо /webhook, но не полагайтесь только на него.
  • Отвечайте быстро, а тяжёлую работу выносите в очередь, чтобы медленные запросы не превращались в повторные доставки.
  • Фильтр по IP-диапазонам Telegram — дополнительный слой; диапазоны могут меняться, поэтому основной проверкой остаётся секретный заголовок.

Детали параметров — в документации setWebhook.

Контроль доступа к админ-функциям

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

Как правильно:

  • Сверяйте user_id со списком разрешённых в конфиге или базе — в каждом админском обработчике, а не только во «входной» команде.
  • Держите проверку в одном месте: фильтр или middleware на весь админский роутер.
  • В группах запрашивайте актуальный статус через getChatMember, а не кешируйте права навсегда. Помните об анонимных админах: их сообщения приходят от имени группы, и from_user — не реальный человек.
  • Логируйте действия админов: кто, что и когда сделал.

Валидация ввода и callback data

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

  • Для SQL — только параметризованные запросы, никакой конкатенации строк.
  • Экранируйте пользовательский текст перед отправкой с parse_mode HTML или MarkdownV2, иначе сломается разметка или появится поддельная ссылка.
  • Проверяйте тип и размер файлов до обработки; не запускайте скачанные файлы.
  • Ограничивайте частоту запросов на пользователя, чтобы один аккаунт не перегрузил бота или платный внешний API.

Отдельное внимание — callback data. Это до 64 байт, которые проходят через клиент, поэтому безопаснее считать, что их можно подделать. Кнопка с order:delete:1542 не доказывает, что заказ 1542 принадлежит этому пользователю.

  • На каждый callback проверяйте на сервере, что объект принадлежит пользователю и действие допустимо в его текущем состоянии.
  • Для чувствительных действий храните контекст на сервере, а в callback кладите только случайный короткий идентификатор.
  • Делайте действия идемпотентными: повторное нажатие не должно дважды списывать деньги или создавать запись.

Хранение пользовательских данных

  • Собирайте минимум: если хватает user_id, не просите номер телефона.
  • Определите сроки хранения: удаляйте старые FSM-состояния, логи и историю диалогов. Ставьте TTL в Redis.
  • Закройте Redis и базу от интернета, используйте пароли и шифрованные бэкапы.
  • Не пишите персональные данные и токены в логи.
  • Проверьте местное законодательство о персональных данных: в ряде стран, включая Узбекистан и Россию, есть требования к локализации данных.

Частые вопросы

Нет. URL утекают через логи, прокси и скриншоты настроек. Секретный заголовок добавляется за минуты и даёт простое правило: нет заголовка — нет обработки.

Поможем с этим

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

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.