24 β Web App xavfsizligi: initData¶
β¬ οΈ Oldingi: 23 β Telegram Web App (Mini App) asoslari Β· π README Β· Keyingi: 25 β Mini App backend β‘οΈ
Bu bobda: O'tgan bobda Mini App'ni ochishni o'rgandik. Endi eng muhim savol: backend ushbu so'rovni AYNAN o'sha foydalanuvchi yuborganiga qanday ishonadi? Javob β
initData. Avval nega client'ga umuman ishonib bo'lmasligini ko'ramiz (har kimcurlbilan soxtauser_idyuborishi mumkin β buni amalda ko'rsatamiz). So'nginitDatanima ekanini (user,auth_date,query_id,hash...) ochib beramiz. Asosiy qism β validatsiya algoritmini bosqichma-bosqich:data_check_string(hash'siz, saralangan,\nbilan),secret = hash_hmac('sha256', $token, 'WebAppData', true),hash = hash_hmac('sha256', $dcs, $secret), vahash_equalsbilan vaqt-doimiy solishtirish; hamdaauth_dateeskirishi (replay hujumi). Ikki yo'lni ko'rsatamiz: qo'lda (har bir bosqich qo'lda) va tayyor Nutgram β$bot->validateWebAppData($initData). DIQQAT (men tasdiqladim, bu juda muhim): Nutgram noto'g'ri data'dafalseQAYTARMAYDI β uInvalidDataExceptionTASHLAYDI, shuning uchun unitry/catchbilan ishlatish SHART. Yana bir muhim topilma: Nutgramauth_date'ni tekshirmaydi β eskirishni siz qo'lda tekshirasiz.Halol eslatma: Bu bobdagi BARCHA validatsiya kodi to'liq OFFLINE β token va internet KERAKMAS β haqiqatan ishga tushirilib tekshirilgan (Nutgram 4.46, PHP 8.4, PHPUnit 12.5). Soxta token bilan to'g'ri
initDataqurib qabul qilinishini, bitta belgi buzilgandaInvalidDataExceptiontashlanishini, va eskiauth_daterad etilishini chinakam RUN qilib ko'rsatamiz (quyida natijalar bilan). Faqat "jonli" qism β Mini App'ning Telegram ilovasi ichida real ochilishi va real qurilmadan kelganinitDataβ illustrativ; algoritm va kod aynan production'da ishlaydigani.
Nega client'ga ishonib bo'lmaydi?¶
Tasavvur qiling, Mini App'ingiz JavaScript'da foydalanuvchi ID'sini oladi va backend'ga shunday yuboradi:
// MINI APP (client) β XAVFLI yondashuv
fetch('/api/balance', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_id: 99281932 }), // <-- bunga ishonib bo'lmaydi!
});
Backend user_id'ni o'qib, o'sha foydalanuvchining balansini qaytaradi. Muammo: client β bu foydalanuvchining qurilmasi, sizniki emas. Har kim brauzer konsolida yoki oddiy curl bilan istalgan qiymatni yuborishi mumkin:
# Hujumchi β hech qanday Mini App'siz, to'g'ridan-to'g'ri:
curl -X POST https://sizning-backend.uz/api/balance \
-H 'Content-Type: application/json' \
-d '{"user_id": 1}' # boshqaning (yoki admin?) ID'si
Hech qanday tekshiruvsiz backend bu so'rovni haqiqiy deb qabul qiladi va begona foydalanuvchining ma'lumotini beradi. Bu β eng keng tarqalgan Mini App zaifligi. Client yuborgan har qanday qiymat (ID, balans, rol, narx) soxta bo'lishi mumkin.
Qoida (oltin): server hech qachon client aytgan "men kimman"ga ishonmaydi. Foydalanuvchi shaxsi server tomonida, ishonchli manbadan tasdiqlanishi kerak. Telegram Mini App'lar uchun bu ishonchli manba β initData.
PHP eslatma: Bu g'oya web'da hammaga taalluqli β
../php/README.mdda sessiya/parol,../laravel/README.mdda CSRF/auth haqida o'qigan bo'lsangiz, "client'ga ishonma" tamoyili tanish. Bu yerda Telegram o'ziga xos mexanizm β HMAC imzo β beradi.
initData nima?¶
Mini App ochilganda Telegram brauzerga maxsus satr beradi: window.Telegram.WebApp.initData. Bu β URL query string ko'rinishidagi imzolangan ma'lumot. Masalan (qisqartirilgan):
user=%7B%22id%22%3A99281932%2C%22first_name%22%3A%22Oqil%22...%7D&auth_date=1781359633&query_id=AAEt-test&hash=d8f0e2195841c89267eda100219f6e4143f946fadd19e368d8f2117b5f8129f1
Asosiy maydonlar:
| Maydon | Ma'no |
|---|---|
user |
JSON: foydalanuvchi haqida (id, first_name, username, language_code...). URL-kodlangan. |
auth_date |
App ochilgan Unix vaqti. Eskirishni tekshirish uchun muhim. |
query_id |
Sessiya ID'si (answerWebAppQuery uchun kerak bo'lishi mumkin). |
chat, receiver, chat_type, chat_instance |
Attachment-menu yoki to'g'ridan-to'g'ri havola orqali ochilganda. |
start_param |
startattach/deep-link parametri. |
hash |
Imzo β qolgan barcha maydonlarning HMAC-SHA256 hash'i. Aynan shu maydon hammasini "qulflaydi". |
Sehr hash maydonida. U bot token bilan hisoblangan. Bot token faqat sizda (va Telegram'da) bor β hujumchida yo'q. Demak, hujumchi user'ni o'zgartirsa, mos hash'ni hisoblay olmaydi. Backend hash'ni qayta hisoblab solishtiradi: mos kelsa β data haqiqiy va o'zgartirilmagan; mos kelmasa β soxta.
Telegram bu mexanizmni rasman hujjatlaydi: https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app.
Validatsiya algoritmi (bosqichma-bosqich)¶
Telegram aniq tartibni belgilaydi. Uni avval qo'lda yozamiz β shunda har bosqich aniq bo'ladi β keyin Nutgram'ning tayyor metodini ko'rsatamiz.
Bosqichlar:
- Query string'ni massivga aylantir.
parse_str()avtomatik URL-dekodlaydi. hash'ni ajratib ol va olib tashla. Qolgan maydonlar imzolanadi.data_check_stringqur: maydonlarni kalit bo'yicha alifbo tartibida sarala, har birinikey=valueqil, so'ng\n(yangi qator) bilan birlashtir. Tartib va\naniq shu β aks holda hash mos kelmaydi.secretkalitini hisobla:hash_hmac('sha256', $token, 'WebAppData', true). Diqqat: bu yerda xabar β bu token, kalit esa β literal'WebAppData'satr, vatrue(xom bayt) qaytaradi.- Yakuniy hash:
hash_hmac('sha256', $dataCheckString, $secret)(hex satr βtruesiz). - Vaqt-doimiy solishtir:
hash_equals($calculated, $remoteHash). (Oddiy===o'rnigahash_equalsβ timing attack'dan himoya.) auth_dateeskirishini tekshir: masalan 24 soatdan eski bo'lsa rad et (replay hujumiga qarshi).
Qo'lda: InitDataGuard sinfi¶
<?php
final class InitDataGuard
{
public static function validate(string $botToken, string $initData, int $maxAgeSec = 86400): array
{
// 1) Query stringni massivga (parse_str avtomatik urldecode qiladi)
parse_str($initData, $data);
// 2) hash'ni ajratamiz va olib tashlaymiz
$remoteHash = (string) ($data['hash'] ?? '');
unset($data['hash']);
// 3) data_check_string: kalit bo'yicha sarala -> "key=value" -> \n bilan birlashtir
ksort($data);
$lines = [];
foreach ($data as $key => $value) {
$lines[] = "$key=$value";
}
$dataCheckString = implode("\n", $lines);
// 4) secret = HMAC-SHA256(token), kalit = "WebAppData", XOM bayt (true)
$secretKey = hash_hmac('sha256', $botToken, 'WebAppData', true);
// 5) hisoblangan hash = HMAC-SHA256(data_check_string), kalit = secret, hex
$calculatedHash = hash_hmac('sha256', $dataCheckString, $secretKey);
// 6) vaqt-doimiy solishtirish (timing attack'dan himoya)
if (!hash_equals($calculatedHash, $remoteHash)) {
throw new RuntimeException('Hash mos kelmadi β soxta yoki buzilgan data');
}
// 7) auth_date eskirishini tekshirish (replay hujumiga qarshi)
$authDate = (int) ($data['auth_date'] ?? 0);
if ($authDate <= 0 || (time() - $authDate) > $maxAgeSec) {
throw new RuntimeException('auth_date eskirgan yoki yo\'q β qaytadan oching');
}
return $data;
}
}
E'tibor bering β bu sinfga bot token KERAK ($botToken). Token tufayli imzo ishonchli. Token kodga yozilmaydi β .env/getenv orqali keladi (11-bobga qarang).
auth_date va replay hujumi¶
hash o'zgarmagan, haqiqiy initData'ni hujumchi qayta-qayta yuborishi mumkin β bu replay hujumi. To'liq himoya auth_date'ni tekshirishdan boshlanadi: agar data juda eski bo'lsa (masalan 24 soatdan) β rad eting. Telegram'ning rasmiy namunasi ham aynan shu yondashuvni tavsiya qiladi. (Yuqori xavfli amallar uchun query_id'ni bir martalik token sifatida bazada belgilab, qayta ishlatilishini bloklash mumkin.)
Tayyor yo'l: Nutgram validateWebAppData¶
Yuqoridagi algoritmni har safar qo'lda yozish shart emas β Nutgram buni bitta metodda beradi:
<?php
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Exception\InvalidDataException;
$bot = new Nutgram($_ENV['BOT_TOKEN']);
try {
// MUVAFFAQIYATda WebAppData obyektini qaytaradi
$webApp = $bot->validateWebAppData($initData);
$userId = $webApp->user->id; // ishonchli β serverda tasdiqlangan
$username = $webApp->user->username;
// ... endi $userId bilan ishonch bilan ishlash mumkin
} catch (InvalidDataException $e) {
// NOTO'G'RIda β false EMAS, EXCEPTION
http_response_code(401);
exit('Yaroqsiz initData');
}
β οΈ ENG MUHIM nuqta (tasdiqlangan):
validateWebAppDatafalseqaytarMAYDI. Data noto'g'ri bo'lsa, uSergiX44\Nutgram\Exception\InvalidDataExceptionTASHLAYDI. Shuning uchun unitry/catchbilan o'rash SHART. Agar sizif (!$bot->validateWebAppData($initData))deb yozsangiz β bu xato: noto'g'ri data'da kodifgacha ham yetib bormaydi, exception bilan to'xtaydi. To'g'ri data'da esa qaytgan obyekt "truthy",ifhech narsa qilmaydi. Demaktry/catchβ yagona to'g'ri yo'l.
Nutgram qaytargan WebAppData obyektida ->user (WebAppUser: id, first_name, username...), ->auth_date (DateTime), ->query_id, ->chat, ->start_param kabi maydonlar bor.
β οΈ Yana bir muhim topilma (men RUN qilib tasdiqladim): Nutgram
validateWebAppDatafaqat HASH'ni tekshiradi β uauth_date'ni TEKSHIRMAYDI. Ya'ni bir yil oldingi haqiqiyinitData'ni bersangiz ham, hash to'g'ri bo'lsa, Nutgram uni qabul qiladi va exception tashlamaydi. Shuning uchun eskirish/replay tekshiruvini SIZ qo'lda qo'shasiz:
Yana bir nuance: Nutgram'ning ichki implementatsiyasi hash solishtirishda strcmp ishlatadi (hash_equals o'rniga). Sizning o'z kodingizda esa har doim hash_equals ishlating β bu vaqt-doimiy va xavfsizroq.
To'liq OFFLINE tekshiruv (token va tarmoq KERAKMAS)¶
Endi eng qiziq qism: hamma narsani haqiqatan ishga tushirib ko'rsatamiz. Soxta (lekin to'g'ri formatdagi) token bilan to'g'ri initData quramiz, uni qabul qildiramiz; bitta belgini buzamiz β InvalidDataException olamiz; eski auth_date bilan β rad ettiramiz. Hech qanday Telegram, token yoki internet kerak emas.
Quyidagi verify.php skripti (qo'lda + Nutgram, ikkalasi ham):
<?php
require 'vendor/autoload.php';
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Exception\InvalidDataException;
// Soxta token (HMAC matematikasi uchun). Nutgram::fake() ham aynan shu tokenni ishlatadi.
$token = '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11';
// To'g'ri initData qurish (Telegram qiladigan ishni biz qilamiz)
function buildInitData(string $token, array $fields): string
{
$pairs = $fields;
ksort($pairs);
$dcs = [];
foreach ($pairs as $k => $v) {
$dcs[] = "$k=$v";
}
$secret = hash_hmac('sha256', $token, 'WebAppData', true);
$fields['hash'] = hash_hmac('sha256', implode("\n", $dcs), $secret);
$qs = [];
foreach ($fields as $k => $v) {
$qs[] = $k . '=' . urlencode($v);
}
return implode('&', $qs);
}
$fields = [
'user' => json_encode(['id' => 99281932, 'first_name' => 'Oqil', 'username' => 'i_oqil']),
'auth_date' => (string) time(),
'query_id' => 'AAEt-test-query',
];
$initData = buildInitData($token, $fields);
// --- Nutgram bilan ---
$bot = Nutgram::fake(); // fake() ichida yuqoridagi token bor
// 1) To'g'ri -> qabul
$webApp = $bot->validateWebAppData($initData);
echo "TO'G'RI -> QABUL. user id={$webApp->user->id}\n";
// 2) Bitta belgini buzamiz -> InvalidDataException
$bad = substr($initData, 0, -1) . 'x';
try {
$bot->validateWebAppData($bad);
echo "XATO: buzilgan qabul qilindi\n";
} catch (InvalidDataException $e) {
echo "BUZILGAN -> InvalidDataException: {$e->getMessage()}\n";
}
// 3) Eski auth_date -> Nutgram QABUL qiladi (tekshirmaydi), qo'lda rad etamiz
$old = buildInitData($token, ['user' => $fields['user'], 'auth_date' => (string)(time() - 100000)]);
$w = $bot->validateWebAppData($old); // exception YO'Q
$ageDays = round((time() - $w->auth_date->getTimestamp()) / 86400, 1);
echo "ESKI: Nutgram qabul qildi (yosh={$ageDays} kun) -> qo'lda rad etish KERAK\n";
Bu skriptning haqiqiy natijasi:
=== 1) Qo'lda validatsiya ===
TO'G'RI initData -> QABUL. user={"id":99281932,"first_name":"Oqil","username":"i_oqil","language_code":"uz"}
BUZILGAN data -> RAD: Hash mos kelmadi β soxta yoki buzilgan data
ESKI auth_date -> RAD: auth_date eskirgan (100000 s)
=== 2) Nutgram validateWebAppData ===
TO'G'RI -> QABUL. user id=99281932, username=i_oqil, auth_date=2026-06-13T14:07:13+00:00
BUZILGAN -> InvalidDataException TASHLANDI: Invalid webapp data
ESKI auth_date: Nutgram QABUL qildi (u auth_date'ni TEKSHIRMAYDI) -> yosh=1.2 kun, qo'lda rad etish KERAK
Bu natija uch narsani chinakam isbotlaydi: (1) to'g'ri initData ikkala usulda ham qabul qilinadi; (2) bitta belgi buzilsa β qo'lda RuntimeException, Nutgram'da InvalidDataException (false emas!); (3) eski auth_date'ni qo'lda guard rad etadi, Nutgram esa qabul qiladi β demak eskirishni o'zingiz tekshirasiz.
PHPUnit bilan mustahkamlash¶
Aynan shu mantiqni avtomatik testga aylantiramiz (16-bobdagi Nutgram::fake() uslubida). Hujumchi boshqa token bilan user_id = 1 yasashga urinsa ham rad etilishini ham sinaymiz:
<?php
use PHPUnit\Framework\TestCase;
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Exception\InvalidDataException;
final class InitDataTest extends TestCase
{
private const TOKEN = '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11';
// ... buildInitData() yordamchisi yuqoridagidek ...
public function test_nutgram_accepts_valid(): void
{
$bot = Nutgram::fake();
$init = $this->buildInitData($this->validFields());
$webApp = $bot->validateWebAppData($init);
$this->assertSame(99281932, $webApp->user->id);
}
public function test_nutgram_throws_on_tampered(): void
{
$bot = Nutgram::fake();
$bad = substr($this->buildInitData($this->validFields()), 0, -1) . 'x';
$this->expectException(InvalidDataException::class); // false EMAS β exception
$bot->validateWebAppData($bad);
}
public function test_manual_rejects_forged_user_with_wrong_token(): void
{
// Hujumchi BOSHQA token bilan user_id=1 yasaydi -> bizning token bilan rad etiladi
$forged = $this->buildInitDataWithToken('WRONG:token-attacker', ['user' => json_encode(['id' => 1])]);
$this->expectException(RuntimeException::class);
InitDataGuard::validate(self::TOKEN, $forged);
}
}
phpunit ning haqiqiy natijasi (7 ta test β qo'lda guard + Nutgram, soxta token hujumi ham):
PHPUnit 12.5.29 by Sebastian Bergmann and contributors.
Runtime: PHP 8.4.0
....... 7 / 7 (100%)
Time: 00:00.049, Memory: 8.00 MB
OK (7 tests, 9 assertions)
Yashil. Bu β botingiz xavfsizligining mustahkam poydevori: har deploy oldidan tokensiz, tarmoqsiz bir soniyada tekshiriladi.
Backend'da to'g'ri qo'llash¶
Production'da qoida sodda: HAR bir Mini App so'rovida initData'ni serverda tekshiring. user_id'ni so'rov tanasidan (body) EMAS, faqat tasdiqlangan initData'dan oling.
<?php
// Mini App backend (illustrativ β Slim/oddiy PHP)
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Exception\InvalidDataException;
function authenticate(Nutgram $bot, string $initData): int
{
try {
$webApp = $bot->validateWebAppData($initData);
} catch (InvalidDataException) {
http_response_code(401);
exit('Yaroqsiz initData');
}
// Eskirishni QO'LDA tekshiramiz (Nutgram tekshirmaydi)
if (time() - $webApp->auth_date->getTimestamp() > 86400) {
http_response_code(401);
exit('initData eskirgan');
}
return $webApp->user->id; // <-- ISHONCHLI manba, faqat shuni ishlat
}
// initData odatda sarlavha yoki body orqali keladi:
$initData = $_SERVER['HTTP_X_INIT_DATA'] ?? ($_POST['initData'] ?? '');
$userId = authenticate($bot, $initData);
// endi $userId bilan ishonch bilan: balans, profil, xarid...
Muhim odatlar:
initData'ni har so'rovda qayta tekshiring β bir marta emas. (Yoki tekshirilgandan keyin o'z sessiya tokeningizni/JWT bering β buni 25-bobda ko'ramiz.)- HTTPS majburiy β
initDatashifrlanmagan kanal orqali ketmasin. - Bot token faqat serverda,
.env'da. Hech qachon client'ga yubormang. - Eskirishni (
auth_date) doim tekshiring.
Keyingi bobda (25 β Mini App backend) shu poydevor ustiga to'liq backend quramiz: marshrutlar, sessiya, ma'lumotlar bazasi bilan birlashtiramiz.
Mashqlar¶
Oson¶
initDataichidagihashmaydoni nima vazifa bajaradi? Nega uni hisoblashda hujumchi takrorlay olmaydi?data_check_stringqurishda maydonlar qaysi tartibda joylashadi va nima bilan birlashtiriladi?validateWebAppDatanoto'g'ri data'da nima qaytaradi βfalsemi yoki boshqa narsami? To'g'ri javobni yozing.- Nega
===o'rnigahash_equalsishlatiladi? secretkalitini hisoblashda HMAC'ning xabari nima, kaliti nima? (hash_hmac('sha256', $token, 'WebAppData', true))- Nega
user_id'ni so'rovbody'sidan olish xavfli,initData'dan olish xavfsiz?
O'rta¶
InitDataGuard::validate()ni yozing va PHPUnit testi bilan: to'g'riinitData-> massiv qaytadi; buzilgan ->RuntimeException.- Berilgan
initData'da bitta belgini o'zgartirib (masalan oxirgisini), Nutgram'ningInvalidDataExceptiontashlashini test bilan isbotlang (expectException). auth_date'ni 100000 soniya orqaga suribinitDataquring vaInitDataGuarduni rad etishini tekshiring.- Nutgram
validateWebAppDataauth_date'ni tekshirmasligini ko'rsatadigan test yozing (eski data -> exception YO'Q, lekin yoshi 24 soatdan katta). authenticate()funksiyasini yozing:try/catch+ eskirish tekshiruvi +user_idqaytarish.- Hujumchi boshqa token bilan
user_id = 1yasashga urinadiganinitDataquring va sizning token bilan rad etilishini test qiling.
Qiyin¶
- To'liq
verify.phpni yozing: bitta skriptda qo'lda + Nutgram, uchchala holat (to'g'ri / buzilgan / eski) β RUN qiling va chiqishini tahlil qiling. query_id'ni bir martalik token sifatida ishlatib replay'ni bloklovchi mexanizm loyihalang (bazada ishlatilganquery_id'larni belgilash). Pseudokod yoki ishlaydigan PHP yozing.- Middleware (9-bob) sifatida
initDatatekshiruvini o'rang: har Mini App so'rovi avtomatik autentifikatsiyadan o'tsin,user_idkeyingi qatlamga uzatilsin.
Yechimlar
Oson 1. hash β qolgan barcha maydonlarning HMAC-SHA256 imzosi, bot token bilan hisoblangan. Hujumchida token yo'q, shuning uchun u user'ni o'zgartirsa, mos hash'ni hisoblay olmaydi β server qayta hisoblaganda mos kelmaydi.
Oson 2. Maydonlar (hash'siz) kalit bo'yicha alifbo tartibida saralanadi, har biri key=value ko'rinishida, va \n (yangi qator) bilan birlashtiriladi.
Oson 3. false QAYTARMAYDI β InvalidDataException tashlaydi. Shuning uchun try/catch SHART; if (!$bot->validateWebAppData(...)) xato yondashuv.
Oson 4. hash_equals β vaqt-doimiy (constant-time) solishtirish. Oddiy === solishtirishda mos kelmagan birinchi baytda to'xtaydi, vaqt farqi orqali hash bayt-bayt taxmin qilinishi mumkin (timing attack). hash_equals bu farqni yo'qotadi.
Oson 5. Xabar β bot token; kalit β literal 'WebAppData' satr; true β natija xom bayt (keyingi HMAC'da kalit sifatida ishlatiladi).
Oson 6. body'dagi har narsa client tomonidan, demak hujumchi tomonidan to'liq nazorat qilinadi (curl bilan istalgan qiymat). initData'dagi user esa Telegram tomonidan imzolangan β server uni token bilan tasdiqlaydi, soxtalashtirib bo'lmaydi.
O'rta 1. Yuqoridagi InitDataGuard sinfidan foydalaning. Test:
public function test_valid(): void
{
$init = $this->buildInitData($this->validFields());
$data = InitDataGuard::validate(self::TOKEN, $init);
$this->assertSame(99281932, json_decode($data['user'], true)['id']);
}
public function test_tampered(): void
{
$bad = substr($this->buildInitData($this->validFields()), 0, -1) . 'x';
$this->expectException(RuntimeException::class);
InitDataGuard::validate(self::TOKEN, $bad);
}
O'rta 2.
public function test_nutgram_throws_on_tampered(): void
{
$bot = Nutgram::fake();
$bad = substr($this->buildInitData($this->validFields()), 0, -1) . 'x';
$this->expectException(InvalidDataException::class);
$bot->validateWebAppData($bad);
}
O'rta 3.
public function test_expired(): void
{
$init = $this->buildInitData($this->validFields(time() - 100000));
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('eskirgan');
InitDataGuard::validate(self::TOKEN, $init);
}
O'rta 4. Bu β bobdagi muhim topilma:
public function test_nutgram_does_not_check_auth_date(): void
{
$bot = Nutgram::fake();
$init = $this->buildInitData($this->validFields(time() - 100000));
$webApp = $bot->validateWebAppData($init); // exception YO'Q
$this->assertGreaterThan(86400, time() - $webApp->auth_date->getTimestamp());
}
O'rta 5.
function authenticate(Nutgram $bot, string $initData, int $maxAge = 86400): int
{
try {
$webApp = $bot->validateWebAppData($initData);
} catch (InvalidDataException) {
http_response_code(401);
exit('Yaroqsiz initData');
}
if (time() - $webApp->auth_date->getTimestamp() > $maxAge) {
http_response_code(401);
exit('initData eskirgan');
}
return $webApp->user->id;
}
O'rta 6.
public function test_forged_with_wrong_token_rejected(): void
{
// hujumchi token'ga ega emas β boshqa token bilan imzolaydi
$forged = $this->buildInitDataWithToken('WRONG:token-attacker', [
'user' => json_encode(['id' => 1, 'first_name' => 'Hacker']),
'auth_date' => (string) time(),
]);
$this->expectException(RuntimeException::class);
InitDataGuard::validate(self::TOKEN, $forged); // bizning token bilan tekshiriladi -> rad
}
Hujumchining hash'i WRONG:token-attacker bilan hisoblangan; biz haqiqiy token bilan qayta hisoblaymiz β mos kelmaydi.
Qiyin 1. Bobdagi to'liq verify.php ni ishlating (qo'lda manualValidate + Nutgram validateWebAppData, uchchala holat). Chiqish: to'g'ri -> QABUL; buzilgan -> RAD/InvalidDataException; eski -> qo'lda RAD, Nutgram QABUL. Asosiy xulosa: hash β yaxlitlik/autentlik; auth_date β eskirish, va uni qo'lda tekshirish kerak.
Qiyin 2. query_id'ni bir martalik (nonce) sifatida:
function authenticateOnce(Nutgram $bot, PDO $db, string $initData): int
{
$webApp = $bot->validateWebAppData($initData); // try/catch real kodda
if (time() - $webApp->auth_date->getTimestamp() > 86400) {
throw new RuntimeException('eskirgan');
}
$qid = $webApp->query_id ?? throw new RuntimeException('query_id yo\'q');
// Allaqachon ishlatilganmi?
$stmt = $db->prepare('SELECT 1 FROM used_queries WHERE query_id = ?');
$stmt->execute([$qid]);
if ($stmt->fetchColumn()) {
throw new RuntimeException('replay β bu query_id allaqachon ishlatilgan');
}
// Belgilab qo'yamiz
$db->prepare('INSERT INTO used_queries (query_id, used_at) VALUES (?, ?)')
->execute([$qid, time()]);
return $webApp->user->id;
}
Eslatma: query_id faqat ba'zi kontekstlarda bo'ladi; umumiy himoya β auth_date + qisqa amal muddati. Eski yozuvlarni vaqti-vaqti bilan tozalang.
Qiyin 3. Middleware:
$bot->middleware(function (Nutgram $bot, $next) {
$initData = $bot->message()?->web_app_data?->data
?? /* yoki HTTP sarlavhasidan, backend kontekstida */ '';
try {
$webApp = $bot->validateWebAppData($initData);
} catch (InvalidDataException) {
return; // to'xtatamiz β keyingi handler chaqirilmaydi
}
if (time() - $webApp->auth_date->getTimestamp() > 86400) {
return;
}
$bot->setUserData('verified_user_id', $webApp->user->id);
$next($bot); // faqat tasdiqlangan so'rov o'tadi
});
Bu naqsh Mini App backend HTTP qatlamida (Slim middleware) ham, bot ichida onWebAppData oldidan ham qo'llanadi. Toza ajratish: tekshiruv bitta joyda, qolgan handlerlar verified_user_id'ga ishonadi.
β¬ οΈ Oldingi: 23 β Telegram Web App (Mini App) asoslari Β· π README Β· Keyingi: 25 β Mini App backend β‘οΈ