Telegram initData можно освоить на одном небольшом проекте, если сразу видеть вход, действие и результат. initData можно превратить в безопасную авторизацию без пароля, если проверить подпись на сервере и ограничить возраст данных. Ниже вы соберёте работающий пример и проверите обычный и граничный сценарии.
Объект initDataUnsafe удобен для интерфейса, но пользователь может подменить его в браузере, поэтому сервер не должен доверять этим полям. Поэтому отделим настройку, входные данные, основное действие и проверку. В этой статье вы соберёте серверную функцию проверки hash и auth_date, которая возвращает подтверждённого пользователя. Проверка: настоящая строка проходит, изменённое поле отклоняется, а старые данные получают отдельную ошибку.
передать Telegram.WebApp.initData на сервер. Не расширяйте проект до первого успешного запуска.
data_check_string воспроизводима. Проверяйте значение, а не отсутствие ошибки.
После успеха добавьте серверную сессию и отдельный ответ для просроченных данных. Так копирование станет практикой.
Как устроен учебный проект Telegram initData
Telegram initData получает входные данные, проверяет их, выполняет одно внешнее действие и сохраняет наблюдаемый результат. Держите ввод, логику и отображение раздельно: тогда каждый этап можно проверить отдельно.
| Этап | Действие | Проверяемый результат |
|---|---|---|
| Клиент | передать Telegram.WebApp.initData на сервер | сырой initData не меняется |
| Строка проверки | отсортировать пары кроме hash | data_check_string воспроизводима |
| Подпись | вычислить HMAC по правилам Telegram | timingSafeEqual подтверждает hash |
| Срок | сравнить auth_date с текущим временем | устаревший запуск отклоняется |
Контрольные точки: клиент, строка проверки, подпись и срок. Следующий шаг начинается после совпадения с ожидаемым результатом.

От входных данных до видимого результата. Сначала проверяется вход, затем действие и только потом интерфейс.
initDataUnsafe.user как доказательство личности. Передавайте исходную строку initData. Сохраните команду запуска и ожидаемый вывод.Собираем рабочий пример шаг за шагом
Материал сверялся с официальной документацией сервиса 21 октября 2026 года. Создайте Node.js проект. BOT_TOKEN храните только на сервере, клиент отправляет initData в теле HTTPS-запроса. Проверьте текущую папку через pwd или Get-Location. Вставьте пример целиком, выполните один раз, затем разбирайте по частям.
import crypto from 'node:crypto'
export function validateInitData(initData, botToken, maxAge = 3600) {
const params = new URLSearchParams(initData)
const receivedHash = params.get('hash')
if (!receivedHash) return { ok: false, reason: 'hash missing' }
params.delete('hash')
const dataCheckString = [...params.entries()]
.sort(([a], [b]) => a.localeCompare(b))
.map(([key, value]) => key + '=' + value)
.join('\n')
const secret = crypto.createHmac('sha256', 'WebAppData')
.update(botToken).digest()
const expected = crypto.createHmac('sha256', secret)
.update(dataCheckString).digest('hex')
const valid = receivedHash.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(receivedHash), Buffer.from(expected))
const authDate = Number(params.get('auth_date'))
const fresh = Number.isFinite(authDate) && Date.now() / 1000 - authDate <= maxAge
if (!valid) return { ok: false, reason: 'bad hash' }
if (!fresh) return { ok: false, reason: 'expired' }
return { ok: true, user: JSON.parse(params.get('user') || 'null') }
}- Оригинальная строка возвращает
ok: trueи разобранного пользователя. - Подмена id, имени или auth_date меняет HMAC и приводит к отказу.
- Повторный запуск даёт тот же предсказуемый результат.
- Пустой ввод получает понятное сообщение вместо падения.
Главная функция принимает явные аргументы и возвращает данные, которые можно проверить отдельно от интерфейса. Найдите в примере вход, проверку и место формирования результата.

Что отличает устойчивый проект от случайного запуска. Явные границы делают ошибку локальной и понятной.
Что происходит внутри и почему это работает
Telegram и ваш сервер знают bot token. Из него выводится secret key, затем HMAC подписывает каноническую строку параметров. Сравнение hash доказывает целостность, но свежесть проверяется отдельно. Логику держите отдельно от вывода: функция получает аргумент и возвращает значение, а интерфейс показывает его. Секреты хранятся в переменных окружения и не попадают в исходники, журналы и скриншоты.
| Часть | За что отвечает | Что проверить |
|---|---|---|
| initData | несёт параметры и подпись | передаётся без изменения |
| HMAC | проверяет целостность строки | hash сравнивается безопасно |
| auth_date | ограничивает повтор старого запуска | есть допустимый возраст |
Успешная подпись не означает, что запрос нельзя повторить. Для чувствительных действий создайте серверную сессию, используйте CSRF-защиту по модели приложения и не принимайте платёжное действие только по одному initData. Не прячьте поток в одном обработчике: сохраните вход, проверьте его, вызовите операцию и только затем обновите интерфейс.
Проверяем проект и добавляем свою функцию
Сделайте endpoint /session, который принимает initData один раз и выдаёт короткую HttpOnly cookie. До правки запишите ожидаемое поведение одним предложением. После граничного теста повторите обычный сценарий.
- Сохраните ожидаемый результат: настоящая строка проходит, изменённое поле отклоняется, а старые данные получают отдельную ошибку.
- Измените правило: добавьте серверную сессию и отдельный ответ для просроченных данных. Сначала предскажите итог.
- Передайте граничное значение и убедитесь, что приложение не падает.
- Верните корректные данные и объясните роли трёх основных частей.
- сырой initData не меняется
- data_check_string воспроизводима
- timingSafeEqual подтверждает hash
- устаревший запуск отклоняется
После изменения повторите основной и ошибочный сценарии, чтобы новое поведение не сломало исходный результат. Полезная правка проходит от нового ввода до видимого результата.

Четыре проверки перед следующим шагом. Каждый следующий этап начинается после наблюдаемого результата.
Что запомнить и куда двигаться дальше
В интерфейсе можно показывать initDataUnsafe, но все права, баланс и действия проверяйте по подтверждённой серверной личности. Для закрепления откройте в REST API на Express, в первом проекте TypeScript и в WebSocket-чате.
Как изучить тему в Кодике
Сначала разберите формат данных и одну функцию, затем подключайте сеть, интерфейс или внешний сервис. Сначала восстановите главную функцию и проверьте её на трёх значениях.
| Шаг | Что разобрать | Мини-проверка |
|---|---|---|
| 1. Основа | URLSearchParams, Buffer и модули Node.js | Объясните вход и выход функции. |
| 2. Инструмент | HMAC, каноническую строку и timingSafeEqual | Повторите команду по памяти. |
| 3. Проект | Endpoint с проверкой подписи и срока | Проверьте два сценария. |
| 4. Рефакторинг | Сессию, cookie и защиту чувствительных действий | Разделите ввод, логику и вывод. |
Закрепите проект, переписав ключевую функцию по памяти и добавив собственный граничный случай. Базовый синтаксис закрепите через курс JavaScript.
Эти данные доступны клиенту и могут быть изменены.
Подпись никогда не совпадёт. Удалите hash до сортировки.
Используйте timingSafeEqual после проверки длины.
Корректную старую строку можно повторять слишком долго.
Что получится в итоге

Финальный экран показывает результат, состояние запроса и понятное сообщение об ошибке без раскрытия секретов.
Что доказывает hash?
Что набор параметров не изменился после формирования Telegram.
Зачем отдельно проверять auth_date?
Подпись не ограничивает срок повторного использования данных.
Что проверять первым, если Telegram initData не запускается?
Версию среды, текущую папку, команду запуска и первую строку ошибки.
Как понять, что проект Telegram initData работает?
Проверьте основной сценарий, неверный ввод, повторный запуск и один граничный случай.
Можно ли начать с готового кода?
Да. Добейтесь результата, измените одно правило и восстановите ключевую функцию без копирования.
Выберите одно улучшение и проведите его от ввода до результата.
Если не хватает основы, перейдите курс JavaScript, затем восстановите ключевую функцию без подсказки.