Злам Telegram-бота далеко не завжди починається зі складної атаки на код. Іноді достатньо токена в старому Git-репозиторії, резервної копії .env, доступної через веб, або процесу, який працює на сервері з надмірними правами.
- Витік токена означає фактичну втрату контролю над ботом
- Секрети потрібно відокремити від коду та файлової частини сайту
- Навіть безпечний код не врятує бота на погано захищеному сервері
- Webhook потрібно захищати так само, як звичайний зовнішній API
- Команди користувача не можна автоматично вважати дозволеними діями
- Даних користувачів потрібно зберігати менше, ніж хочеться розробнику
- Логи мають допомагати розслідуванню, а не створювати новий витік
- Перевіряти безпеку бота потрібно як ланцюжок, а не одним тестом
Тому безпеку бота варто розглядати як ланцюжок: секрети, сервер, webhook, авторизація дій, дані користувачів і журнали. Якщо один із цих рівнів слабкий, захист решти не гарантує безпеки всієї системи.
Нижче — практичний маршрут аудиту: що саме перевіряти, які симптоми мають насторожити та які дії виконати, якщо проблема вже знайдена.
Витік токена означає фактичну втрату контролю над ботом
Якщо токен Telegram-бота став відомий сторонній особі, його потрібно не «сховати назад», а перевипустити. Токен Bot API працює як секрет автентифікації: той, хто його отримав, може звертатися до API від імені бота без додаткового пароля чи коду підтвердження.
Де токен найчастіше опиняється випадково
Очевидний ризик — токен, записаний прямо в коді. Але на практиці секрети часто витікають через менш помітні місця: старі коміти Git, debug-логи, архіви проєкту, резервні копії конфігурації, історію shell, Docker Compose або CI/CD-конфігурації. Видалити токен із поточного файлу недостатньо — Git добре пам’ятає те, про що розробник уже забув.
Для первинної перевірки варто шукати не лише саме значення токена, а й назви змінних, якими його зазвичай позначають:
grep -RE "BOT_TOKEN|TELEGRAM_TOKEN" /path/to/project
git grep "BOT_TOKEN"
git log -p --all
Окремо перевірте, чи не потрапляє .env до Git:
git status
git check-ignore .env
Що робити після підозри на витік
Якщо токен міг опинитися назовні хоча б ненадовго, вважайте його скомпрометованим. Далі без експериментів: перевипустіть токен через BotFather, замініть секрет у робочому середовищі, перезапустіть процес бота та перегляньте журнали на предмет незвичних дій. Видалення секрету з файлу без ротації токена не закриває ризик, бо копія могла вже залишитися у сторонньої особи або в автоматичному індексі.
Секрети потрібно відокремити від коду та файлової частини сайту
Токен бота, пароль бази даних і сторонні API-ключі не повинні «їздити» разом із кодом. Якщо секрети записані в Python-, PHP- чи Node.js-файлах, будь-яка копія проєкту автоматично стає копією облікових даних.
Змінні середовища та .env
Для невеликого проєкту поширений варіант — передавати секрети через змінні середовища або файл .env. Але сам факт наявності .env нічого не захищає. Файл не повинен лежати у публічній директорії вебсервера, потрапляти до репозиторію чи резервних копій, доступних через HTTP.
Для сервісу під systemd зручно використовувати окремий файл середовища, який читає лише потрібний системний користувач. Базова перевірка прав виглядає так:
ls -la
stat .env
chmod 600 .env
chown botuser:botuser .env
Секрети в Docker і CI/CD
У контейнерному середовищі варто уникати жорстко записаних секретів у Dockerfile або файлах, які комітяться разом із проєктом. Для CI/CD краще використовувати сховище секретів самої системи та не виводити значення змінних у build-лог. Production і test також варто розділяти: один і той самий токен або пароль у двох середовищах збільшує площу ризику.
Добра перевірка тут дуже проста: код можна передати розробнику або зберегти в репозиторії, не передаючи разом із ним робочі секрети. Якщо це неможливо, межа між кодом і конфігурацією проведена неправильно.
Навіть безпечний код не врятує бота на погано захищеному сервері
Telegram-бот не повинен працювати з надмірними системними правами. Якщо процес запущено від root, вразливість у самому застосунку потенційно дає атакувальнику набагато більше можливостей, ніж потрібно боту для нормальної роботи.
SSH і системний користувач
Для бота доцільно створити окремого Linux-користувача, дати йому доступ лише до каталогу застосунку та запускати сервіс через systemd від його імені. Для адміністративного SSH краще використовувати ключі, обмежити прямий вхід root і не залишати парольну авторизацію «про всяк випадок», якщо вона не потрібна.
У sshd_config варто перевірити параметри PermitRootLogin і PasswordAuthentication, а після змін — переконатися, що новий спосіб входу справді працює, перш ніж закривати поточну сесію.
Порти, firewall і зайві сервіси
Спочатку подивіться, що реально слухає мережу й запущено в системі:
ss -tulpn
systemctl --type=service --state=running
ufw status
ufw status доречний, якщо на сервері використовується UFW; для nftables або firewalld потрібно перевірити відповідні правила. Список відкритих портів має бути коротким і зрозумілим: SSH, вебсервер для webhook та лише ті служби, які реально потрібні. Не варто відкривати порт просто тому, що так швидше під час налагодження. Якщо база даних використовується тільки локально, їй зазвичай не потрібен публічний доступ з Інтернету.
Безпека залежить і від способу розгортання: чим менше ручних операцій із секретами та файлами, тим нижчий ризик випадкової помилки. Такий підхід із винесенням керування ботом у вебпанель використовується, зокрема, на сайті UkrLine, але сама панель не замінює захист ОС, контроль прав, firewall та регулярні оновлення.
Для журналу конкретного процесу корисна команда:
journalctl -u bot.service
Якщо бот приймає файли, запускає фонові задачі або працює з БД, принцип найменших привілеїв особливо важливий: процес має отримувати рівно ті права, без яких він не може виконати свою функцію.
Webhook потрібно захищати так само, як звичайний зовнішній API
HTTPS сам по собі не робить webhook Telegram-бота захищеним від сторонніх запитів. TLS захищає канал, але не вирішує питання, кому дозволено стукати в endpoint. Застосунок усе одно повинен відрізняти очікуваний запит від довільного POST, надісланого на ту саму адресу.
Secret token для webhook
Під час налаштування webhook доцільно задати secret token і перевіряти заголовок X-Telegram-Bot-Api-Secret-Token до обробки update. Якщо значення не збігається, запит має завершуватися помилкою 4xx без запуску бізнес-логіки.
Перевірка повинна стояти на початку ланцюжка. Якщо застосунок спочатку парсить великий body, звертається до БД і лише потім перевіряє секрет, захисний механізм працює запізно.
Rate limiting і контроль запиту
Для webhook корисно обмежити розмір request body, додати rate limiting на рівні Nginx або застосунку та не зберігати повний вміст кожного запиту в логах без потреби. Це не заміна перевірці secret token, а окремий шар захисту від шуму, помилкових інтеграцій і простих спроб перевантаження endpoint.
Найкраще перевірити це не очима в конфігурації, а запитом. Наприклад, для тестового endpoint:
curl -i -X POST https://bot.example.com/webhook \
-H "Content-Type: application/json" \
-H "X-Telegram-Bot-Api-Secret-Token: correct-secret" \
-d '{"update_id":1}'
curl -i -X POST https://bot.example.com/webhook \
-H "Content-Type: application/json" \
-H "X-Telegram-Bot-Api-Secret-Token: wrong-secret" \
-d '{"update_id":1}'
Другий запит має отримати 4xx і не доходити до обробника подій бота. Саме це підтверджує, що контроль доступу реально працює, а не просто присутній у коді.
Команди користувача не можна автоматично вважати дозволеними діями
Callback-кнопка Telegram не є механізмом контролю доступу. Те, що користувач не бачить адміністративної кнопки в інтерфейсі, не означає, що серверна функція захищена. Авторизацію потрібно перевіряти на бекенді під час кожної привілейованої операції.
user_id, роль і дозвіл — не одне й те саме
user_id допомагає ідентифікувати користувача, але цього недостатньо для складнішого бота. Потрібно окремо визначити ролі та набір дозволених дій: наприклад, оператор може переглядати заявки, але не змінювати налаштування або видаляти дані.
Для невеликого службового бота allowlist із конкретними user_id може бути достатнім, однак перевірка має виконуватися перед кожною адміністративною дією. Не варто будувати захист лише навколо команди /admin, прихованого меню або значення callback_data.
Критичні операції
Видалення даних, зміна реквізитів, запуск зовнішніх задач або інші ризикові операції краще підтверджувати окремо. Для багатокрокових сценаріїв можна використовувати короткоживучий стан або одноразову ознаку підтвердження, щоб стара кнопка не запускала дію через тривалий час після створення.
Перевірити логіку варто негативним тестом: сформувати той самий виклик від користувача без потрібної ролі та переконатися, що сервер відмовляє незалежно від того, як саме була викликана функція — командою, callback або іншим маршрутом.
Даних користувачів потрібно зберігати менше, ніж хочеться розробнику
Найбезпечніші персональні дані — ті, які бот не зберігає без потреби. Якщо функції достатньо user_id і статусу заявки, немає сенсу роками накопичувати повні тексти повідомлень, телефони, файли та службові копії «на майбутнє».
Мінімізація даних
Для кожного поля в БД має бути проста відповідь на два питання: навіщо воно потрібне та скільки часу його треба зберігати. Якщо відповіді немає, поле варто переглянути. Компрометація БД не може розкрити дані, яких у ній ніколи не було.
Паролі, ключі та доступ до БД
Паролі користувачів не слід зберігати у відкритому вигляді або «шифрувати так, щоб потім розшифрувати». Для паролів потрібне стійке хешування, наприклад Argon2id або bcrypt. Токени, API-ключі та інші секрети мають зберігатися окремо від звичайних даних і бути доступними лише тим процесам, яким вони справді потрібні.
Обліковий запис БД для бота також не повинен мати зайвих прав. Якщо застосунку не потрібне створення нових користувачів БД або адміністративні операції, таких дозволів у його облікового запису бути не повинно.
Резервні копії теж містять дані
Backup часто випадає з аудиту, хоча це ще одна копія тієї самої бази. Потрібно перевірити, де зберігаються резервні копії, хто має до них доступ, чи не потрапляють вони в публічні каталоги та як довго зберігаються. Якщо основну БД чистять через 30 днів, а архіви лежать роками, політика видалення фактично не працює.
Логи мають допомагати розслідуванню, а не створювати новий витік
Debug-лог корисний рівно до моменту, поки сам не стає витоком. BOT_TOKEN, паролі, cookie, приватні ключі, заголовки Authorization і повні тіла приватних повідомлень не повинні записуватися «для зручності».
Що варто залишати в журналі
Для більшості інцидентів достатньо часу події, типу операції, результату, коду помилки, технічного ідентифікатора користувача за потреби та запису про відмову в авторизації. Такі дані дозволяють відновити послідовність подій, не копіюючи в лог увесь вміст запиту.
Швидкий аудит журналів можна почати з пошуку очевидних маркерів:
grep -REi "token|authorization|password" /var/log/
Результати потрібно переглядати вручну: саме слово authorization ще не означає витік. Мета — знайти фактичні значення секретів або надмірно детальні дампи.
Ротація та права доступу
Для файлових логів налаштовують ротацію, наприклад через logrotate; для systemd-сервісів контролюють журнали через journalctl. Окремо перевіряють права читання та строк зберігання. Лог, який роками росте без ротації, одночасно створює ризик витоку та ризик заповнення диска.
Вимикати журналювання повністю теж не варто. Після інциденту без логів складно зрозуміти, чи була проблема одиничним збоєм, помилкою конфігурації або реальною спробою несанкціонованої дії.
Перевіряти безпеку бота потрібно як ланцюжок, а не одним тестом
Первинний аудит Telegram-бота варто проходити послідовно: токен → сервер → webhook → права користувачів → дані → логи. Компрометація будь-якого одного компонента може зробити захист інших рівнів недостатнім, тому перевірка лише BOT_TOKEN або firewall дає хибне відчуття безпеки.
| Проблема | Що під ризиком | Як перевірити | Перша дія |
|---|---|---|---|
| Токен у Git | Керування Bot API | git log, git grep |
Перевипустити токен |
.env доступний через веб |
Токени, БД, API-ключі | HTTP-перевірка, права файлу | Закрити доступ і змінити секрети |
| SSH із паролем для root | Увесь сервер | sshd_config |
Обмежити root і перейти на ключі |
| Webhook без secret token | Endpoint бота | Тестовий POST-запит | Додати перевірку секрету |
| Права перевіряються не завжди | Привілейовані функції | Негативний тест іншим акаунтом | Додати server-side авторизацію |
| Секрети в логах | Токени й приватні дані | grep журналів |
Маскування та ротація |
Короткий чек-лист перед запуском або після оновлення
- Токен відсутній у репозиторії та старих публічних архівах.
.envне доступний через веб і має обмежені права.- Бот працює від окремого системного користувача, а не від root.
- SSH налаштований через ключі, зайві порти закриті firewall.
- ОС, бібліотеки та залежності регулярно оновлюються.
- Webhook працює через HTTPS і перевіряє secret token до обробки update.
- Привілейовані дії щоразу перевіряють
user_idі роль. callback_dataне використовується як доказ дозволу на операцію.- Бот не накопичує дані, які не потрібні його функціям.
- База даних не відкрита в Інтернет без реальної потреби.
- Резервні копії мають окремий контроль доступу та строк зберігання.
- У логах немає токенів, паролів, Authorization headers та інших секретів.
- Налаштована ротація журналів і можна побачити помилки авторизації та падіння процесу.
Такий аудит не замінює повноцінного тестування безпеки складної системи, але добре відсіює типові конфігураційні помилки. Якщо після перевірки кожен рівень має зрозумілу відповідь на питання «хто має доступ, навіщо і як це контролюється», захист бота вже не тримається на одному випадково добре налаштованому компоненті.


