Валидация пользователей WebAPP в Telegram, почему не работает?

Ссылка скопирована
1 ответ

Есть 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-&gt;sha256($bot_token, self::WEB_APP_DATA_CONST);         $hash_data = $this-&gt;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-&gt;sha256($bot_token, self::WEB_APP_DATA_CONST); $hash_data = $this-&gt;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', () =&gt; {   validateData(initData) })  function validateData(data) {   // как по мне лучше сразу передать данными и не парcить это в PHP, так проще и можно валидировать.    // axios.post('/{your_controller_validate_date}', data).then(response =&gt; {})      // оставлю вариант с передачей строки в initData   axios.post('/{your_controller_validate_date}', {initData: data})     .then(response =&gt; {  console.log(response.data) }) }

import axios from "axios"; const { initData } = window.Telegram.WebApp document.addEventListener('DOMContentLoaded', () =&gt; { validateData(initData) }) function validateData(data) { // как по мне лучше сразу передать данными и не парcить это в PHP, так проще и можно валидировать. // axios.post('/{your_controller_validate_date}', data).then(response =&gt; {}) // оставлю вариант с передачей строки в initData axios.post('/{your_controller_validate_date}', {initData: data}) .then(response =&gt; { 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&amp;chat_instance=-3312718360795391383&amp;chat_type=sender&amp;auth_date=1703053222&amp;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)-&gt;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&amp;chat_instance=-3312718360795391383&amp;chat_type=sender&amp;auth_date=1703053222&amp;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)-&gt;handle('YOUR_BOT_TOKEN', $data); // true/false $isValid = $data_hash == $hash;

Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Лучший ответ
1
Антон С. Ответ

Для начала, давайте разберемся, какая именно проблема возникает при валидации пользователей WebAPP в Telegram. Возможно, у вас есть проблема с получением данных от пользователей или с проверкой их правильности. Для того чтобы решить эту проблему, следует убедиться, что ваш код корректно обрабатывает запросы от пользователей и правильно проверяет их данные.

Вот пример простого кода на PHP для валидации пользователей в Telegram:

&lt;?php
// Получаем данные от пользователя
$update = json_decode(file_get_contents(&#039;php://input&#039;), true);

// Проверяем, что данные получены корректно
if (!$update) {
    exit;
}
 
// Получаем ID пользователя
$user_id = $update[&#039;message&#039;][&#039;from&#039;][&#039;id&#039;];

// Проверяем, что ID пользователя корректный
if (!$user_id) {
    exit;
}
 
// Пример валидации имени пользователя
$user_name = $update[&#039;message&#039;][&#039;from&#039;][&#039;first_name&#039;];

if (empty($user_name)) {
    sendMessage($user_id, &#039;Пожалуйста, укажите свое имя.&#039;);
    exit;
}
 
// Пример отправки сообщения
function sendMessage($chat_id, $message) {
    $url = &#039;https://api.telegram.org/bot/sendMessage?chat_id=' . $chat_id . '&amp;text=' . urlencode($message);
    file_get_contents($url);
}
?&gt;

&lt;?php // Получаем данные от пользователя $update = json_decode(file_get_contents(&#039;php://input&#039;), true); // Проверяем, что данные получены корректно if (!$update) { exit; } // Получаем ID пользователя $user_id = $update[&#039;message&#039;][&#039;from&#039;][&#039;id&#039;]; // Проверяем, что ID пользователя корректный if (!$user_id) { exit; } // Пример валидации имени пользователя $user_name = $update[&#039;message&#039;][&#039;from&#039;][&#039;first_name&#039;]; if (empty($user_name)) { sendMessage($user_id, &#039;Пожалуйста, укажите свое имя.&#039;); exit; } // Пример отправки сообщения function sendMessage($chat_id, $message) { $url = &#039;https://api.telegram.org/bot/sendMessage?chat_id=' . $chat_id . '&amp;text=' . urlencode($message); file_get_contents($url); } ?&gt;

В данном примере мы получаем данные от пользователя, проверяем их на корректность и валидируем имя пользователя. Если данные не проходят валидацию, отправляем сообщение пользователю с просьбой исправить ошибку.

Помимо этого, убедитесь, что у вас настроены все необходимые параметры в настройках бота в Telegram и что ваш сервер корректно обрабатывает запросы от Telegram.

Если проблема все еще остается, попробуйте добавить логирование в ваш код, чтобы отследить, где именно возникает ошибка, и устранить ее. Не забывайте также проверять документацию Telegram API для более подробной информации о валидации пользователей.

Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно