Mini Apps
Validating initData
Когда Mini App открывается, Telegram передаёт initData — строку с данными пользователя, чата и параметрами запуска. Эта строка подписана ботом, и ваш бэкенд должен проверить подпись, прежде чем доверять данным.
Структура initData
Строка — это URL-encoded query string:
query_id=abc123&user=%7B%22id%22%3A123456%2C%22first_name%22%3A%22Ivan%22%7D&auth_date=1700000000&hash=abcdef123456...
Поле hash — это HMAC-SHA256 от остальных параметров, отсортированных по ключу. Секретный ключ формируется из токена бота.
Алгоритм проверки
- Парсим строку, удаляем параметр
hash. - Сортируем оставшиеся пары по ключу (лексикографически).
- Склеиваем в строку
key=value\n(перевод строки между парами). - Вычисляем
secret_key = HMAC_SHA256("WebAppData", bot_token). - Вычисляем
calculated_hash = HMAC_SHA256(data_string, secret_key). - Сравниваем
calculated_hashс полученнымhash(hex-строка). - Проверяем
auth_date— не старше 24 часов (или вашего TTL).
PHP-реализация
function verifyInitData(string $initData, string $botToken, int $ttl = 86400): ?array {
parse_str($initData, $params);
if (!isset($params['hash'])) {
return null;
}
$hash = $params['hash'];
unset($params['hash']);
ksort($params);
$dataString = '';
foreach ($params as $k => $v) {
$dataString .= $k . '=' . $v . "\n";
}
$dataString = rtrim($dataString, "\n");
$secretKey = hash_hmac('sha256', 'WebAppData', $botToken, true);
$calculatedHash = hash_hmac('sha256', $dataString, $secretKey);
if (!hash_equals($calculatedHash, $hash)) {
return null;
}
if (isset($params['auth_date']) && (time() - (int)$params['auth_date'] > $ttl)) {
return null;
}
if (isset($params['user'])) {
$params['user'] = json_decode($params['user'], true);
}
return $params;
}
// Использование
$initData = $_POST['initData'] ?? '';
$botToken = '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11';
$userData = verifyInitData($initData, $botToken);
if ($userData === null) {
http_response_code(401);
exit('Invalid initData');
}
// $userData['user']['id'], $userData['user']['first_name'] и т.д.
Типичные ошибки
- Не сортируют параметры — порядок критичен.
- Используют строковый токен вместо бинарного ключа —
hash_hmac(..., true)возвращает raw binary дляsecret_key. - Не проверяют auth_date — реплей-атаки.
- Доверяют user без парсинга JSON — в initData это URL-encoded JSON-строка.
Передача на бэкенд
В Mini App (JS) получаете initData через Telegram.WebApp.initData и отправляете на свой API:
fetch('https://api.example.com/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ initData: Telegram.WebApp.initData })
});
Бэкенд проверяет, создаёт/обновляет пользователя, выдаёт JWT или сессию.