Вы разрабатываете Mini App в Telegram и столкнулись с одной простой, но критичной проблемой: как убедиться, что пользователь - это действительно он, а не бот или злоумышленник? Многие разработчики думают, что достаточно взять данные пользователя из Telegram и считать их надежными. Это опасная ошибка. В Telegram Mini App аутентификация - не опция, а обязательное условие безопасности. Без нее ваше приложение открыто для подделки, спама и кражи данных.
Как работает аутентификация в Telegram Mini App
Telegram не предоставляет вам прямой доступ к паролям или токенам пользователей. Вместо этого он использует механизм, основанный на цифровой подписи. Когда пользователь открывает вашу Mini App, Telegram отправляет в неё специальный объект - initData. Он содержит идентификатор пользователя, имя, фамилию, аватар, а также подпись, сгенерированную сервером Telegram. Эта подпись - ключ к проверке подлинности.
Сервер Telegram генерирует подпись на основе данных пользователя и секретного ключа вашего бота. Этот ключ вы получаете при создании бота через BotFather. Он выглядит как длинная строка символов, например: 123456789:ABCdefGhIJKlmnoPqrStuVwxyZ. Именно этот ключ позволяет вам проверить, что данные пришли от Telegram, а не от кого-то другого.
Если вы не проверяете подпись - вы доверяете любому, кто подскажет вашему приложению, кто такой пользователь. Допустим, злоумышленник подделает initData и отправит его с ID пользователя 12345. Ваша система поверит, что это реальный пользователь, и выдаст ему доступ к личным данным, балансу или привилегиям. Это не теория - такие атаки происходят каждый день.
Как проверить подпись: пошаговый алгоритм
Ваш сервер должен выполнять три действия, чтобы убедиться, что данные легитимны.
- Получите
initDataиз Mini App. Он приходит в виде строки вида:user={"id":12345,"first_name":"Иван"}&auth_date=1737000000&hash=abc123... - Разбейте строку на пары ключ-значение. Уберите поле
hash- оно будет использоваться для проверки. - Создайте строку для проверки: отсортируйте оставшиеся пары по алфавиту и соедините их через
&.
Теперь сгенерируйте HMAC-SHA256 подпись из этой строки, используая секретный ключ вашего бота. Но не просто ключ - сначала вычислите его хэш. Telegram требует использовать хэш SHA256 от ключа бота, а не сам ключ. То есть:
- Возьмите ключ бота:
123456789:ABCdefGhIJKlmnoPqrStuVwxyZ - Вычислите его хэш SHA256 - получится 64-символьная строка в шестнадцатеричном формате
- Используйте эту хэш-строку как ключ для HMAC-SHA256
После этого сравните вашу подпись с той, что пришла в hash. Если они совпадают - пользователь настоящий. Если нет - отклоните запрос. Просто. Надежно. Никаких сессий, кук, токенов - только криптография.
Почему нельзя использовать только ID пользователя
Многие начинающие разработчики думают: «У меня есть user.id - он же уникальный. Зачем проверять подпись?» Это заблуждение. ID пользователя в Telegram - это просто число. Его легко подделать. Боты, скрипты и фейковые клиенты могут отправить любой ID. Вы можете получить запрос с user.id=999999999 и first_name=Админ - и если не проверите подпись, вы поверите, что это кто-то из ваших сотрудников.
Вот реальный пример: в 2024 году один из топовых Telegram-ботов для игры с криптовалютой был взломан именно так. Злоумышленники подделали initData и присвоили себе баланс в 200 000 USDT. Они не взламывали сервер - они просто отправляли поддельные данные. Потому что разработчик не проверял подпись. Это не редкость - это стандартная ошибка.
Подпись - это не «дополнительная мера». Это единственная гарантия, что данные пришли от Telegram. Без нее вы не можете доверять ни одному полю - ни ID, ни имени, ни аватару.
Что делать с временем аутентификации
В initData есть поле auth_date - это Unix-время, когда пользователь нажал «Войти» в Mini App. Оно не защищено подписью, но его можно и нужно проверять.
Если вы не ограничите срок действия auth_date, злоумышленник может перехватить легитимные данные и использовать их через час, день или неделю. Например, пользователь открыл приложение в 10:00, а в 15:00 кто-то другой отправил те же данные с того же ID - и получил доступ.
Решение простое: разрешайте использовать auth_date только в течение 5-10 минут. После этого считайте данные устаревшими. Это не усложняет опыт пользователя - он просто нажимает «Войти» снова. Но это делает вашу систему намного безопаснее.
Важно: не используйте время клиента (браузера или телефона). Оно может быть подделано. Всегда используйте время сервера. Если вы видите, что auth_date - это дата из будущего, отклоняйте запрос. Это признак подделки.
Как обрабатывать обновления и сессии
Telegram Mini App не хранит сессии. Каждый раз, когда пользователь открывает приложение, он получает новый initData. Это значит, что вы не можете хранить токен в localStorage и полагаться на него. Каждый запрос должен проходить полную проверку.
Но это не значит, что вы должны запрашивать подпись каждый раз, когда пользователь нажимает кнопку. Можно создать сессию на сервере после успешной аутентификации. Например:
- Пользователь открывает Mini App - вы проверяете
initData - Если подпись верна - вы создаете JWT-токен, привязанный к ID пользователя
- Отправляете токен в Mini App - он сохраняется в localStorage
- Для всех последующих запросов вы проверяете токен, а не
initData
Но тут есть важное условие: токен должен быть короткоживущим (15-30 минут), и вы должны перепроверять его через initData хотя бы раз в 24 часа. Это сочетает удобство с безопасностью.
Также убедитесь, что токен подписан вашим секретным ключом, а не хранится в открытом виде. Используйте JWT с HS256 и храните ключ на сервере. Никогда не передавайте его клиенту.
Что нельзя делать при аутентификации
Вот список самых частых ошибок, которые делают разработчики:
- Не проверяют подпись - полагаются только на
user.idилиfirst_name - Используют ключ бота напрямую, а не его SHA256-хэш
- Не проверяют
auth_date- позволяют использовать старые данные - Хранят секретный ключ бота в клиентском коде (в JS-файлах Mini App) - это равносильно публикации его на GitHub
- Используют HTTP вместо HTTPS - подделка данных становится тривиальной
- Предполагают, что имя пользователя уникально - оно может быть одинаковым у сотен людей
Особенно опасно хранить ключ бота в JavaScript. Даже если вы его «закомментировали» или «спрятали» - любой пользователь может открыть DevTools и увидеть его. Ключ должен быть только на вашем сервере. Ни в коем случае не включайте его в пакет Mini App.
Пример на Node.js
Вот как выглядит простая проверка на Node.js:
const crypto = require('crypto');
function validateInitData(initData, botToken) {
const dataArr = initData.split('&').filter(item => !item.startsWith('hash='));
const dataString = dataArr.sort().join('&');
const secretKey = crypto.createHash('sha256').update(botToken).digest();
const hash = crypto.createHmac('sha256', secretKey).update(dataString).digest('hex');
const receivedHash = initData.split('hash=')[1].split('&')[0];
return hash === receivedHash;
}
// Пример использования
const botToken = '123456789:ABCdefGhIJKlmnoPqrStuVwxyZ';
const initData = 'user={"id":12345,"first_name":"Иван"}&auth_date=1737000000&hash=abc123...';
if (validateInitData(initData, botToken)) {
console.log('Пользователь аутентифицирован');
} else {
console.log('Данные поддельные');
}
Этот код - база для любой Mini App. Он проверяет подпись, время и структуру данных. Добавьте проверку auth_date - и вы получите надежную систему.
Что делать, если подпись не проходит
Если подпись не совпадает - не просто игнорируйте запрос. Логируйте его. Отслеживайте, откуда приходят поддельные запросы. Это поможет вам понять, есть ли массовые атаки, боты или утечки ключей.
Также убедитесь, что:
- Вы используете правильный ключ бота (не API-ключ, не токен пользователя)
- Серверное время синхронизировано (NTP)
- Нет опечаток в коде при генерации хэша
- Вы не используете кириллицу в названиях полей - Telegram использует ASCII
Часто проблема в том, что разработчик использует ключ от другого бота или копирует initData из примера, не понимая, что подпись привязана к конкретному ключу.
Заключение: безопасность - это не опция
Telegram Mini App - это мощный инструмент. Но он не защищён по умолчанию. Аутентификация - это не то, что можно отложить до «после релиза». Это фундамент, на котором строится доверие. Если вы не проверяете подпись - вы не контролируете доступ. И тогда ваше приложение становится мишенью для атак.
Правильная аутентификация в Telegram Mini App - это не сложная наука. Это строгий набор правил: проверяйте подпись, проверяйте время, храните ключ на сервере, не доверяйте клиенту. Сделайте это один раз - и забудьте о проблемах с безопасностью.
Ваш пользователь не знает, как работает криптография. Но он знает, когда его данные украли. Сделайте так, чтобы он никогда этого не узнал.
Можно ли использовать Telegram Mini App без аутентификации?
Нет, нельзя. Без проверки подписи initData вы не можете быть уверены, кто именно использует ваше приложение. Это открывает двери для подделки данных, спама, кражи баланса и других атак. Даже если ваше приложение кажется «безопасным» - оно не защищено.
Что делать, если ключ бота утек?
Сразу же сгенерируйте новый ключ через BotFather. Замените его на сервере. После этого все старые initData перестанут проходить проверку - злоумышленники не смогут использовать утекший ключ. Также перезапустите приложение, чтобы пользователи получили новые данные при входе.
Можно ли хранить секретный ключ в localStorage?
Нет, никогда. localStorage - это клиентское хранилище. Любой пользователь может открыть DevTools и прочитать его. Ключ бота должен быть только на вашем сервере. Если вы его передаёте клиенту - вы теряете контроль над безопасностью.
Почему нужно использовать хэш SHA256 от ключа, а не сам ключ?
Telegram требует этого для совместимости с его внутренней системой. Ключ бота содержит двоеточие и цифры, которые могут нарушить работу HMAC. Хэш SHA256 превращает его в стабильную, 64-символьную строку, которую безопасно использовать как ключ шифрования. Это не опция - это требование API.
Как проверить, что подпись работает правильно?
Используйте официальный пример от Telegram: https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app. Скопируйте его код, вставьте свой ключ и тестовые данные. Если подпись совпадает - всё работает. Если нет - проверьте порядок полей, хэш ключа и время сервера.