Валидация пользователей WebAPP в Telegram, почему не работает?
Есть php скрипт, который 100% рабочий, пытаюсь при помощи него реализовать проверку данных пользователей WebAPP
получаю данные при помощи Telegram.WebApp.initData
user=%7B%22id%22%3A491735603%2C%22first_name%22%3A%22%F0%9F%85%BD%F0%9F%85%B0%EF%B8%8F%F0%9F%86%83%F0%9F%86%82%22%2C%22last_name%22%3A%22%22%2C%22username%22%3A%22mtNATS%22%2C%22language_code%22%3A%22ru%22%2C%22allows_write_to_pm%22%3Atrue%7D&chat_instance=-3312718360795391383&chat_type=sender&auth_date=1703053222&hash=05dd0f9552a0f12ea994e3616fccf18b0f4c151269664314d9dfd38202e431c5 |
user=%7B%22id%22%3A491735603%2C%22first_name%22%3A%22%F0%9F%85%BD%F0%9F%85%B0%EF%B8%8F%F0%9F%86%83%F0%9F%86%82%22%2C%22last_name%22%3A%22%22%2C%22username%22%3A%22mtNATS%22%2C%22language_code%22%3A%22ru%22%2C%22allows_write_to_pm%22%3Atrue%7D&chat_instance=-3312718360795391383&chat_type=sender&auth_date=1703053222&hash=05dd0f9552a0f12ea994e3616fccf18b0f4c151269664314d9dfd38202e431c5
пытаюсь отправить их при помощи js скрипта
const initDataString = Telegram.WebApp.initData; const serverURL = "/auth.php"; function sendToServer(data) { const xhr = new XMLHttpRequest(); xhr.open("GET", `${serverURL}?${data}`); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log("Server response:", xhr.responseText); } else { console.error("Error:", xhr.status, xhr.statusText); } } }; xhr.send(); } sendToServer(initDataString); |
const initDataString = Telegram.WebApp.initData; const serverURL = "/auth.php"; function sendToServer(data) { const xhr = new XMLHttpRequest(); xhr.open("GET", `${serverURL}?${data}`); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log("Server response:", xhr.responseText); } else { console.error("Error:", xhr.status, xhr.statusText); } } }; xhr.send(); } sendToServer(initDataString);
function checkTelegramAuthorization($auth_data) { $check_hash = $auth_data['hash']; $_SESSION['hash'] = $check_hash; unset($auth_data['hash']); $data_check_arr = []; foreach ($auth_data as $key => $value) { $data_check_arr[] = $key . '=' . $value; } sort($data_check_arr); $data_check_string = implode("n", $data_check_arr); $secret_key = hash('sha256', BOT_TOKEN, true); $hash = hash_hmac('sha256', $data_check_string, $secret_key); if (strcmp($hash, $check_hash) !== 0) { throw new Exception('Data is NOT from Telegram'); } if ((time() - $auth_data['auth_date']) > 86400) { throw new Exception('Data is outdated'); } return $auth_data; } |
function checkTelegramAuthorization($auth_data) { $check_hash = $auth_data['hash']; $_SESSION['hash'] = $check_hash; unset($auth_data['hash']); $data_check_arr = []; foreach ($auth_data as $key => $value) { $data_check_arr[] = $key . '=' . $value; } sort($data_check_arr); $data_check_string = implode("n", $data_check_arr); $secret_key = hash('sha256', BOT_TOKEN, true); $hash = hash_hmac('sha256', $data_check_string, $secret_key); if (strcmp($hash, $check_hash) !== 0) { throw new Exception('Data is NOT from Telegram'); } if ((time() - $auth_data['auth_date']) > 86400) { throw new Exception('Data is outdated'); } return $auth_data; }
в ответ получаю
Data is NOT from Telegram
помогите добиться результата! Спасибо!
Дополнительно:
неправильно вычисляется secret_key, т.к. согласно документации он тоже должен быть чем-то вроде
hash_hmac('sha256', BOT_TOKEN, 'WebAppData', true); |
hash_hmac('sha256', BOT_TOKEN, 'WebAppData', true);
- это код с git на который ссылается офф док...
ща попробую - mtNATS, прямо в доке написано псевдокодом:
data_check_string = ... secret_key = HMAC_SHA256(<bot_token>, "WebAppData") if (hex(HMAC_SHA256(data_check_string, secret_key)) == hash) { // data is from Telegram }
data_check_string = ... secret_key = HMAC_SHA256(<bot_token>, "WebAppData") if (hex(HMAC_SHA256(data_check_string, secret_key)) == hash) { // data is from Telegram }
да и словами в доке описано то же самое
так что ссылка из доки к сожалению ничего не гарантирует
- IvanU7n, душа! обнял, приподнял, поцеловал, обратно поставил!
дай бог тебе всего хорошего! - А какой код в итоге рабочий?
$secret_key = hash_hmac('sha256', BOT_TOKEN, 'WebAppData', true);
$hash = hash_hmac('sha256', $data_check_string, $secret_key);
if (strcmp($hash, $check_hash) !== 0) {
$return = 'Data is NOT from Telegram';
}Не работает
- vetton, в BOT_TOKEN должен лежать токен бота, в остальном код рабочий
- IvanU7n, про токен то естественно.
$data_check_string имеет такой формат
auth_date=1705675078
query_id=AAHtQ6EQAAAAAO1DoRAXdGkT
user=%7B%22id%22%3A279004141%2C%22first_name%22%3A%22%D0%95%D0%B2%D0%B3%D0%B5%D0%BD%D0%B8%D0%B9%22%2C%22last_name%22%3A%22%D0%9F%D0%B5%D1%82%D1%80%D0%BE%D0%B2%22%2C%22username%22%3A%22johnvetton%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%7Dи с этим кодом выдаёт "Data is NOT from Telegram". Т.к $hash и $check_hash разные
P.S
Я и через Javascript подготавливал $data_check_string и через php потом этим способом (думал может ошибка в подготовке строки)
$data_check_arr = [];
foreach ($auth_data as $key => $value) {
$data_check_arr[] = $key . '=' . $value;
}
sort($data_check_arr);
$data_check_string = implode("n", $data_check_arr);и "true" убирал в конце hash_hmac('sha256', BOT_TOKEN, 'WebAppData', true);
ничего не помогает
- vetton, сейчас проверить негде, но выглядит подозрительной url-кодировка для user
- IvanU7n, да, как я понял в ней и было дело. Нужно было rawurldecode сделать.
Нашел рабочий код, может кому пригодится<?php
$bot_token = '0123456789:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
$data_check_string = 'XXX'; // get from Telegram.WebAppData
$data_check_arr = explode('&', rawurldecode($data_check_string));
$needle = 'hash=';
$check_hash = FALSE;
foreach($data_check_arr AS &$val){
if(substr($val, 0, strlen($needle)) === $needle){
$check_hash = substr_replace($val, '', 0, strlen($needle));
$val = NULL;
}
}$data_check_arr = array_filter($data_check_arr);
sort($data_check_arr);$data_check_string = implode("n", $data_check_arr);
$secret_key = hash_hmac('sha256', $bot_token, "WebAppData", true);
$hash = bin2hex(hash_hmac('sha256', $data_check_string, $secret_key, true) );if(strcmp($hash, $check_hash) === 0){
print('ok');
} else {
print('fail');
}
Ответы:
Хешер данных
class HasherDataAction { const WEB_APP_DATA_CONST = 'WebAppData'; public function handle(string $bot_token, ValidateData $data): string { $data_check = (array) $data; ksort($data_check, SORT_NATURAL); $data_check_string = urldecode(http_build_query($data_check, arg_separator: "n")); $secret_key = $this->sha256($bot_token, self::WEB_APP_DATA_CONST); $hash_data = $this->sha256($data_check_string, $secret_key); return bin2hex($hash_data); } private function sha256(string $data, string $key) { return hash_hmac('sha256', $data, $key, true); } } |
class HasherDataAction { const WEB_APP_DATA_CONST = 'WebAppData'; public function handle(string $bot_token, ValidateData $data): string { $data_check = (array) $data; ksort($data_check, SORT_NATURAL); $data_check_string = urldecode(http_build_query($data_check, arg_separator: "n")); $secret_key = $this->sha256($bot_token, self::WEB_APP_DATA_CONST); $hash_data = $this->sha256($data_check_string, $secret_key); return bin2hex($hash_data); } private function sha256(string $data, string $key) { return hash_hmac('sha256', $data, $key, true); } }
ValidateData для передачи данных в метод хеширования
namespace WebAppDomainData; readonly class ValidateData { public function __construct( public string $query_id, public string $user, public string $auth_date ) { } } |
namespace WebAppDomainData; readonly class ValidateData { public function __construct( public string $query_id, public string $user, public string $auth_date ) { } }
JS
import axios from "axios"; const { initData } = window.Telegram.WebApp document.addEventListener('DOMContentLoaded', () => { validateData(initData) }) function validateData(data) { // как по мне лучше сразу передать данными и не парcить это в PHP, так проще и можно валидировать. // axios.post('/{your_controller_validate_date}', data).then(response => {}) // оставлю вариант с передачей строки в initData axios.post('/{your_controller_validate_date}', {initData: data}) .then(response => { console.log(response.data) }) } |
import axios from "axios"; const { initData } = window.Telegram.WebApp document.addEventListener('DOMContentLoaded', () => { validateData(initData) }) function validateData(data) { // как по мне лучше сразу передать данными и не парcить это в PHP, так проще и можно валидировать. // axios.post('/{your_controller_validate_date}', data).then(response => {}) // оставлю вариант с передачей строки в initData axios.post('/{your_controller_validate_date}', {initData: data}) .then(response => { console.log(response.data) }) }
// данные из JS, $_POST['initData'] $initData = 'user=%7B%22id%22%3A491735603%2C%22first_name%22%3A%22%F0%9F%85%BD%F0%9F%85%B0%EF%B8%8F%F0%9F%86%83%F0%9F%86%82%22%2C%22last_name%22%3A%22%22%2C%22username%22%3A%22mtNATS%22%2C%22language_code%22%3A%22ru%22%2C%22allows_write_to_pm%22%3Atrue%7D&chat_instance=-3312718360795391383&chat_type=sender&auth_date=1703053222&hash=05dd0f9552a0f12ea994e3616fccf18b0f4c151269664314d9dfd38202e431c5'; parse_str($initData, $data_array); // если отправляете данными, это не нужно, у вас сразу будет массив с данными в $_POST; // $data_array = $_POST $hash = $data_array['hash']; $data = new ValidateData($data_array['query_id'], $data_array['user'], $data_array['auth_date']); $data_hash = (new HasherDataAction)->handle('YOUR_BOT_TOKEN', $data); // true/false $isValid = $data_hash == $hash; |
// данные из JS, $_POST['initData'] $initData = 'user=%7B%22id%22%3A491735603%2C%22first_name%22%3A%22%F0%9F%85%BD%F0%9F%85%B0%EF%B8%8F%F0%9F%86%83%F0%9F%86%82%22%2C%22last_name%22%3A%22%22%2C%22username%22%3A%22mtNATS%22%2C%22language_code%22%3A%22ru%22%2C%22allows_write_to_pm%22%3Atrue%7D&chat_instance=-3312718360795391383&chat_type=sender&auth_date=1703053222&hash=05dd0f9552a0f12ea994e3616fccf18b0f4c151269664314d9dfd38202e431c5'; parse_str($initData, $data_array); // если отправляете данными, это не нужно, у вас сразу будет массив с данными в $_POST; // $data_array = $_POST $hash = $data_array['hash']; $data = new ValidateData($data_array['query_id'], $data_array['user'], $data_array['auth_date']); $data_hash = (new HasherDataAction)->handle('YOUR_BOT_TOKEN', $data); // true/false $isValid = $data_hash == $hash;
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос
Для начала, давайте разберемся, какая именно проблема возникает при валидации пользователей WebAPP в Telegram. Возможно, у вас есть проблема с получением данных от пользователей или с проверкой их правильности. Для того чтобы решить эту проблему, следует убедиться, что ваш код корректно обрабатывает запросы от пользователей и правильно проверяет их данные.
Вот пример простого кода на PHP для валидации пользователей в Telegram:
<?php // Получаем данные от пользователя $update = json_decode(file_get_contents('php://input'), true); // Проверяем, что данные получены корректно if (!$update) { exit; } // Получаем ID пользователя $user_id = $update['message']['from']['id']; // Проверяем, что ID пользователя корректный if (!$user_id) { exit; } // Пример валидации имени пользователя $user_name = $update['message']['from']['first_name']; if (empty($user_name)) { sendMessage($user_id, 'Пожалуйста, укажите свое имя.'); exit; } // Пример отправки сообщения function sendMessage($chat_id, $message) { $url = 'https://api.telegram.org/bot/sendMessage?chat_id=' . $chat_id . '&text=' . urlencode($message); file_get_contents($url); } ?>
В данном примере мы получаем данные от пользователя, проверяем их на корректность и валидируем имя пользователя. Если данные не проходят валидацию, отправляем сообщение пользователю с просьбой исправить ошибку.
Помимо этого, убедитесь, что у вас настроены все необходимые параметры в настройках бота в Telegram и что ваш сервер корректно обрабатывает запросы от Telegram.
Если проблема все еще остается, попробуйте добавить логирование в ваш код, чтобы отследить, где именно возникает ошибка, и устранить ее. Не забывайте также проверять документацию Telegram API для более подробной информации о валидации пользователей.