Телеграм как обычно изобрели велосипед, вместо уже всем привычного JWT сделали…
👁 454↗ 10
Вот тут их мануал: core.telegram.org
Получается так, у нас есть объект Telegram.WebApp в расширенном объекте window и выглядит он по этому интерфейсу:
interface WebAppData {
initData: string;
initDataUnsafe: {
user: User;
chat_instance: string;
chat_type: string;
auth_date: string;
hash: string;
};
version: string;
platform: string;
colorScheme: string;
themeParams: ThemeParams;
isExpanded: boolean;
viewportHeight: number;
viewportStableHeight: number;
isClosingConfirmationEnabled: boolean;
isVerticalSwipesEnabled: boolean;
headerColor: string;
backgroundColor: string;
BackButton: Button;
MainButton: MainButton;
SettingsButton: Button;
HapticFeedback: Record<string, unknown>;
CloudStorage: Record<string, unknown>;
BiometricManager: BiometricManager;
}
Нас интересует строки: initData и hash. Где initData представляет собой URL-encoded строку query-параметров.
И чтобы нам наладить работу между Ботом <-> Telegram Server <-> Backend, нам надо сделать эту коммуникацию безопасной, а значит нам надо как-то проверять, что запрос который пришел на backend, действительно от пользователя, а не от третьей стороны. Для этого мы должны использовать следующий алгоритм:
1. Декодировать строку initData, используя URL-encoding
2. Полученные пары ключ-значения сортируем в алфавитном порядке
3. Исключаем ключ hash
4. Из полученных пар составляем тело вида: auth_date=<auth_date>\nquery_id=<query_id>\nuser=<user>
5. Берем хэш от токена вашего бота с помощью алгоритма HMAC-SHA256, добавив дополнительный ключ 'WebAppData'
6. Берем хэш от полученного в шаге 4 тела с помощью того же алгоритма, а в качестве ключа используем хэш, полученный ранее в виде последовательности байтов (ВАЖНО! не hex-репрезентации!)
7. Сраниванием хэши.
На самом деле не сложно, просто много текста, поэтому далее я написал подробно с комментами функцию валидации на JS:
function validateTelegramData(initData, botToken) {
// Шаг 1: Декодируем строку
const decodedData = querystring.parse(decodeURIComponent(initData));
// Шаг 2: Получаем пары ключ-значение и сортируем их в алфавитном порядке
const sortedKeys = Object.keys(decodedData).sort();
// Шаг 3: Исключаем ключ 'hash'
sortedKeys.splice(sortedKeys.indexOf('hash'), 1);
// Шаг 4: Формируем строку для проверки
const dataCheckString = sortedKeys
.map(key => `${key}=${decodedData[key]}`)
.join('\n');
// Шаг 5: Берем хэш от токена вашего бота с помощью алгоритма HMAC-SHA256
const secretKey = crypto.createHmac('sha256', 'WebAppData')
.update(botToken)
.digest();
// Шаг 6: Берем хэш от полученного в шаге 4 тела с помощью того же алгоритма
const calculatedHash = crypto.createHmac('sha256', secretKey)
.update(dataCheckString)
.digest('hex');
// Шаг 7: Преобразуем полученный хэш в hex-строку и сравниваем со значением ключа 'hash'
return calculatedHash === decodedData.hash;
}
Вот и все, далее по аналогии с JWT, если true, то данные валидны и значит это пользователь, если false, то данные скомпрометированы третьей стороной.
Оставлю это здесь, ибо я запарился искать инфу как это сделать, надеюсь я вам сэкономлю кучу времени в будущем 👍
!💬Телеграм | !📹Youtube📎 файл — готовится…
17