Вход в мини-приложение Telegram и MAX без пароля: как это работает
Мини-приложение узнаёт человека по подписи, которую ставит мессенджер. Объясняю простым языком, как сервер проверяет initData токеном бота, зачем ограничивать срок по auth_date, как склеить профиль из Telegram, MAX и сайта и что делать в PWA.
Коротко (TL;DR)
- Мини-приложение в Telegram и MAX узнаёт человека без пароля: мессенджер при запуске передаёт строку данных о пользователе, подписанную на основе токена вашего бота.
- Сервер обязан проверить эту подпись сам. Данным, которые прислал браузер без проверки, верить нельзя: их может подменить кто угодно.
- Подпись не протухает сама. Срок жизни входа задаёт сервер по полю auth_date, и для этого часы сервера должны идти точно.
- Номер телефона, которым человек поделился через мессенджер, помогает склеить один профиль из Telegram, MAX и сайта.
- Вне мессенджера, в PWA или браузере, подписи нет: нужен отдельный вход, например ссылка с кодом через бота.
Сделаю под ключ: разберу задачу, назову срок и цену. Чтобы начать, напишите в Telegram одной фразой.
Разработка мини-приложений TelegramВход в мини-приложение без пароля работает так: когда человек открывает приложение внутри Telegram или MAX, мессенджер передаёт ему строку initData с данными пользователя и подписью hash. Приложение отправляет эту строку на ваш сервер, сервер пересчитывает подпись с помощью токена бота и сравнивает. Совпало, значит данные выдал мессенджер и человек тот, за кого себя выдаёт. Не совпало, значит запрос отклоняется.
Почему пароль здесь не нужен
Человек уже вошёл в мессенджер со своего телефона. Заставлять его придумывать ещё один пароль для записи к врачу или для отчёта руководителю значит терять людей на первом экране. Мессенджер может подтвердить личность сам, и владельцу бизнеса остаётся правильно принять это подтверждение.
Я делал такой вход в трёх проектах, и в каждом он решал немного разную задачу. В проекте для врача-косметолога мини-приложение записи открывается из бота в Telegram, и каждый запрос к его API подписан данными запуска, сервер проверяет подпись. В приложении руководителя команды одна схема проверки работает и для Telegram, и для MAX. В учёте для бухгалтерской фирмы вход в мини-приложение тоже идёт по подписи Telegram, и поддельная подпись отклоняется.
Как работает подпись initData
Если совсем просто: у вашего бота есть токен, секретная строка, которую знают только мессенджер и ваш сервер. Мессенджер берёт данные о пользователе (кто он, когда открыл приложение и другие поля) и вычисляет из них и токена контрольную сумму. Подделать её без токена нельзя. Сервер делает то же вычисление у себя и сравнивает результат.
По документации Telegram, на сервер нужно отправлять именно строку из поля Telegram.WebApp.initData, а проверять её на стороне бота (core.telegram.org/bots/webapps, раздел про проверку данных из мини-приложения). В MAX схема описана в разделе про валидацию данных: мессенджер передаёт стартовые параметры при каждом запуске, те же данные доступны как window.WebApp.initData, а параметр hash обязателен (dev.max.ru/docs/webapps/validation).
Техническая врезка для разработчика
Алгоритм в обоих мессенджерах по документации совпадает по смыслу:
- Разобрать строку initData на пары «ключ=значение», вынуть hash и запомнить его.
- Остальные пары отсортировать по ключу и склеить в строки «ключ=значение» через перевод строки. В MAX документация отдельно напоминает декодировать значения из URL, если платформа этого ещё не сделала.
- Секретный ключ: HMAC-SHA256 от токена бота, где ключом служит строка WebAppData.
- Подпись: HMAC-SHA256 от склеенной строки на секретном ключе, в шестнадцатеричном виде. Сравнить с hash.
secret = HMAC_SHA256(key = "WebAppData", message = BOT_TOKEN)
check = hex(HMAC_SHA256(key = secret, message = data_check_string))
if check != hash: ответить 401
if now - auth_date > лимит: ответить 401
У Telegram есть ещё поле signature для проверки третьей стороной по открытому ключу Ed25519, без токена бота. Для собственного сервера достаточно проверки hash.
Почему нельзя верить данным с клиента
В мини-приложении есть удобный объект initDataUnsafe: те же данные, уже разобранные в JSON. Им пользуются, чтобы показать имя на экране. Обе документации прямо предупреждают, что для проверки он не годится. У Telegram сказано, что данным из этого поля не стоит доверять и использовать на сервере можно только initData после проверки. У MAX, что объект нельзя использовать для валидации данных (dev.max.ru/docs/webapps/bridge).
Причина простая. Всё, что пришло из браузера, можно отправить вручную: открыть адрес API и подставить чужой идентификатор пользователя. Если сервер берёт id из тела запроса и верит ему, любой человек прочитает чужие записи или поставит поручение от имени руководителя. Поэтому правило у меня жёсткое: личность определяется только на сервере, только из проверенной строки, а права проверяются при каждом действии.
Если вы заказываете мини-приложение и хотите понять, сделано ли так у вас, попросите разработчика показать, где именно сервер проверяет hash. Это пять минут разговора. Если сомнения остаются, такую проверку я делаю в рамках технического аудита.
Срок auth_date и часы сервера
Внутри подписанных данных есть поле auth_date: время в секундах, когда человек открыл приложение. Документация Telegram советует дополнительно проверять это поле, чтобы не принимать устаревшие данные. Документация MAX говорит, что auth_date входит в подписанные данные и подделать его нельзя, но конкретного срока не задаёт. Значит, лимит выбираете вы сами: насколько долго строка запуска считается свежей.
Без такой проверки перехваченная строка запуска остаётся рабочей сколько угодно. С ней появляется другая забота: часы сервера. Если сервер спешит или отстаёт, свежая подпись вдруг выглядит старой, и пользователи получают отказ без видимой причины. Время на сервере я синхронизирую автоматически и при отказе пишу в журнал разницу между auth_date и текущим временем, чтобы такую ошибку было видно сразу.
Склейка профиля по номеру телефона
Telegram и MAX дают разные идентификаторы пользователя. Если часть команды сидит в одном мессенджере, а часть в другом, или один человек заходит из обоих, без склейки у вас появятся два профиля одного сотрудника.
Склеивать удобно по номеру телефона, которым человек сам поделился. В Telegram для этого есть метод WebApp.requestContact: мессенджер показывает своё окно с просьбой поделиться номером, и номер получает бот. В MAX есть window.WebApp.requestContact(): он возвращает телефон вместе с authDate и hash, и сервер проверяет этот hash токеном бота так же, как initData. Номер, набранный вручную в поле формы, для склейки я не использую: его никто не подтвердил.
Так сделано в приложении для руководителя команды: номер телефона склеивает профили одного человека из двух мессенджеров и сайта, а поручения и сводка общие. Про то, что ещё отличает мини-приложение в MAX, я писал в статье о мини-аппах в MAX для бизнеса.
Что делать в PWA и браузере
Когда приложение открывают с компьютера в браузере или иконкой на главном экране телефона, мессенджера рядом нет и подписи тоже нет. Тут нужен отдельный вход, и я использую два варианта.
- Вход через бота. Сайт показывает ссылку с одноразовым кодом, человек открывает её в мессенджере, нажимает «Поделиться номером», и сайт сам понимает, что вход подтверждён. Так устроен вход на сайт в приложении руководителя команды.
- Логин и пароль, если с системой работают сотрудники за компьютером. В учёте для бухгалтерской фирмы так входят бухгалтеры: пароли хранятся через PBKDF2, на вход даётся пять попыток за десять минут. Клиенты фирмы при этом заходят из бота по подписи.
Само веб-приложение в обоих случаях одно: кабинет на компьютере, PWA на телефоне и мини-приложение в мессенджере. Как сделать из сайта PWA, разбирал в отдельной статье.
Типичные ошибки
- 401 на каждый запрос. Чаще всего строку проверяют не в том виде: декодировали дважды, потеряли поле, отсортировали не по ключу или считают ключ из токена другого бота. Проверьте, что токен тот же, что у бота, который открывает приложение.
- 401 через несколько минут или у части людей. Слишком короткий лимит на auth_date или неточные часы сервера.
- Доверие initDataUnsafe. Интерфейс работает, но безопасности нет: сервер берёт id из запроса как есть.
- Токен в журналах. В запросах к API бота токен стоит прямо в адресе, и HTTP-библиотека пишет его в лог. В двух проектах мне пришлось приглушить журнал такой библиотеки. Утёкший токен ломает всю схему: им можно подписать любые данные.
- Telegram Desktop и адрес страницы. Telegram дописывает к адресу свой фрагмент с данными запуска, и роутер на хешах в учёте для бухфирмы ломался. Пришлось чистить адрес и водить переходы по-своему. Ещё окно Telegram может держать старую версию страницы после выкладки.
Как я это делаю
Мой порядок для любого мини-приложения с входом:
- Одна функция проверки подписи на сервере для Telegram и MAX, через неё проходит каждый запрос к API.
- Лимит на auth_date и синхронизация времени на сервере, отказ пишется в журнал с причиной.
- Права проверяются на сервере при каждом действии. Интерфейс только прячет кнопки.
- Склейка профилей по подтверждённому номеру телефона, если входов больше одного.
- Отдельный вход для браузера и PWA: через бота или по паролю с ограничением попыток.
- Токен бота не попадает в логи, автотесты проверяют, что поддельная и просроченная подпись отклоняются.
Если нужно мини-приложение для команды или клиентов, посмотрите разработку мини-приложений Telegram; под MAX делаю на той же базе.
Сколько стоит
Цены по моему прайсу:
- Бот в мессенджере: от 30 000 ₽, от 1 недели.
- Бот для бизнеса с мини-приложением, оплатой, записью или CRM: от 80 000 ₽, от 2 недель.
- PWA-приложение с кабинетом в браузере и на телефоне: от 150 000 ₽, от 1 месяца.
- Технический аудит готового приложения, включая проверку входа и прав: от 50 000 ₽, от 1 недели.
- Разработка по часам, если нужно только поправить проверку подписи: от 3 000 ₽ в час.
Частые вопросы
Человеку нужно что-то вводить при входе?
В мессенджере нет. Он открывает приложение из бота, и сервер узнаёт его по подписи. Номер телефона спрашивается одной кнопкой, только если он нужен для склейки профилей или для связи.
Одинаково ли это работает в Telegram и MAX?
По документации обоих мессенджеров схема проверки одна: HMAC-SHA256 с ключом WebAppData и токеном бота. Детали передачи данных отличаются, поэтому код проверки я тестирую на живых клиентах каждого мессенджера.
Что будет, если украдут токен бота?
Тогда можно подделать подпись любого пользователя. Токен нужно сразу перевыпустить, а сервер обновить. Поэтому токен хранится только на сервере и не пишется в журналы.
Можно ли войти с компьютера без мессенджера?
Да, через отдельный вход: ссылка с кодом через бота или логин и пароль. Данные при этом общие с мини-приложением.
Коротко о главном
Вход без пароля в мини-приложение держится на одной проверке: сервер пересчитывает подпись initData токеном бота и не верит ничему, что пришло из браузера без подписи. Срок жизни входа задаёт сервер по auth_date, и для этого его часы должны идти точно. Номер телефона, подтверждённый мессенджером, склеивает профили, а для PWA и браузера нужен свой вход.
Если хотите проверить вход в своём мини-приложении или сделать новое, напишите мне в Telegram слово «ПОДПИСЬ».
Всё о мессенджере MAX в одном месте
Эта статья — часть кластера материалов о MAX: разработка ботов и мини-аппов, продвижение канала, воронки, отраслевые сценарии и кейсы с реальными результатами.
Мини-приложение с надёжным входом
Напишите одной фразой, что нужно. Отвечу, что подойдёт, сколько займёт и сколько стоит. Сообщение уже подготовлено.
- Проверка подписи initData на сервере для Telegram и MAX
- Один профиль по номеру телефона из двух мессенджеров и сайта
- Вход в PWA и браузере через бота или по паролю
- Аудит входа и прав в готовом приложении
Разборы, кейсы и практика — без воды. Выходит регулярно, читать 3–5 минут.


