5-bo‘lim
HMAC va xabar butunligi
Xabar o'zgartirilmaganini va kimdan kelganini isbotlash - vebxuk imzolari misolida.
Ushbu bo‘lim mundarijasi
Xesh ma'lumot o'zgarganini aniqlaydi. Lekin u kim o'zgartirganini ayta olmaydi.
HMAC (Hash-based Message Authentication Code) shu bo'shliqni to'ldiradi: u maxfiy kalit talab qiladi, demak uni faqat kalit egasi hisoblay oladi.
Muammo: yolg'iz xesh yetarli emas #
function korsat(string $nom, string $qiymat): void
{
printf("%-12s %s%s", $nom . ':', $qiymat, PHP_EOL);
}
// Server xabar va uning xeshini yuboradi
$xabar = 'foydalanuvchi=oddiy&rol=user';
$xesh = hash('sha256', $xabar);
korsat('xabar', $xabar);
korsat('xesh', substr($xesh, 0, 32) . '...');
// Yo'lda turgan hujumchi ikkalasini ham almashtiradi
$soxta = 'foydalanuvchi=oddiy&rol=admin';
$soxtaXesh = hash('sha256', $soxta);
echo PHP_EOL, 'hujumchi ozgartirgandan keyin:', PHP_EOL;
korsat('xabar', $soxta);
korsat('xesh', substr($soxtaXesh, 0, 32) . '...');
// Qabul qiluvchi tekshiradi - va hech narsa sezmaydi
echo PHP_EOL, 'tekshiruv otdimi: ',
hash('sha256', $soxta) === $soxtaXesh ? 'HA - ALDANDI' : 'yoq', PHP_EOL;
xabar: foydalanuvchi=oddiy&rol=user
xesh: a63aa56ffc56eec02b4d569034d2873e...
hujumchi ozgartirgandan keyin:
xabar: foydalanuvchi=oddiy&rol=admin
xesh: 70a03c26b55edcb902a2881b0756c44c...
tekshiruv otdimi: HA - ALDANDI
Muammo aniq: hash('sha256', ...) ni har kim hisoblay
oladi. Xabarni o'zgartirgan hujumchi xeshni ham qayta
hisoblab qo'yadi.
Xesh faqat tasodifiy buzilishlardan himoya qiladi:
| Nimadan himoyalaydi | Nimadan himoyalamaydi |
|---|---|
| Diskdagi xatolik | Ataylab o'zgartirish |
| Tarmoqda buzilgan bayt | Yo'ldagi hujumchi |
| Yarim yuklangan fayl | Soxta yuboruvchi |
Autentiklik uchun sir kerak - va uni bilmagan odam kodni hisoblay olmasligi kerak.
Yechim: HMAC #
$kalit = 'umumiy-maxfiy-kalit';
$xabar = 'foydalanuvchi=oddiy&rol=user';
$mac = hash_hmac('sha256', $xabar, $kalit);
echo 'HMAC: ', $mac, PHP_EOL;
// Hujumchi xabarni o'zgartirdi, lekin kalitni bilmaydi
$soxta = 'foydalanuvchi=oddiy&rol=admin';
// U eski MAC ni qoldiradi
echo PHP_EOL, 'soxta xabar + eski MAC:', PHP_EOL;
var_dump(hash_equals($mac, hash_hmac('sha256', $soxta, $kalit)));
// Yoki kalitsiz yangi MAC hisoblamoqchi bo'ladi
$taxmin = hash_hmac('sha256', $soxta, 'taxmin-qilingan-kalit');
echo PHP_EOL, 'taxminiy kalit bilan:', PHP_EOL;
var_dump(hash_equals(hash_hmac('sha256', $soxta, $kalit), $taxmin));
HMAC: 40b8fb8595897d1884bc61630658763d40248187cef8681637a444e803eb53b8
soxta xabar + eski MAC:
bool(false)
taxminiy kalit bilan:
bool(false)
Ikkala urinish ham muvaffaqiyatsiz. Kalitsiz to'g'ri MAC hosil qilib bo'lmaydi.
HMAC ning ichki tuzilishi #
// HMAC(K, m) = H( (K ⊕ opad) || H( (K ⊕ ipad) || m ) )
function qoldaHmacSha256(string $xabar, string $kalit): string
{
$blok = 64; // SHA-256 blok o'lchami - 64 bayt
// uzun kalit avval xeshlanadi, qisqasi nol bilan to'ldiriladi
if (strlen($kalit) > $blok) {
$kalit = hash('sha256', $kalit, true);
}
$kalit = str_pad($kalit, $blok, "\x00");
$ipad = str_repeat("\x36", $blok) ^ $kalit;
$opad = str_repeat("\x5c", $blok) ^ $kalit;
return hash('sha256', $opad . hash('sha256', $ipad . $xabar, true));
}
$xabar = 'test-xabar';
$kalit = 'maxfiy';
$qolda = qoldaHmacSha256($xabar, $kalit);
$tayyor = hash_hmac('sha256', $xabar, $kalit);
echo 'qo\'lda: ', substr($qolda, 0, 32), '...', PHP_EOL;
echo 'tayyor: ', substr($tayyor, 0, 32), '...', PHP_EOL;
var_dump(hash_equals($qolda, $tayyor));
qo'lda: ebda4604c9799481b55bfccea84d81da...
tayyor: ebda4604c9799481b55bfccea84d81da...
bool(true)
ipad (0x36) va opad (0x5c) - doimiy to'ldirgichlar.
Kalit ular bilan XOR qilinadi va xesh ikki marta
chaqiriladi.
Bu tuzilish uzunlikni kengaytirish hujumini imkonsiz qiladi: hujumchi ichki xeshning holatini bilsa ham, tashqi xeshni kalitsiz davom ettira olmaydi.
Yuqoridagi kod - faqat tushuntirish uchun. Real kodda
har doim hash_hmac ishlating: u tezroq va xatosiz.
Amaliy misol: vebxuk imzosi #
final class VebxukImzosi
{
public function __construct(private readonly string $kalit) {}
// Nimani imzolashni ANIQ belgilaymiz: vaqt + nuqta + tana
private function asos(int $vaqt, string $tana): string
{
return $vaqt . '.' . $tana;
}
public function imzola(string $tana, int $vaqt): string
{
$mac = hash_hmac('sha256', $this->asos($vaqt, $tana), $this->kalit);
return "t={$vaqt},v1={$mac}";
}
public function tekshir(
string $tana,
string $sarlavha,
int $hozir,
int $oyna = 300,
): string {
// 1. sarlavhani ajratamiz
$qismlar = [];
foreach (explode(',', $sarlavha) as $juft) {
[$k, $v] = array_pad(explode('=', $juft, 2), 2, '');
$qismlar[$k] = $v;
}
if (!isset($qismlar['t'], $qismlar['v1'])) {
return 'RAD: sarlavha shakli notogri';
}
$vaqt = (int) $qismlar['t'];
// 2. eskirganini tekshiramiz (takroriy uzatishdan himoya)
if (abs($hozir - $vaqt) > $oyna) {
return 'RAD: imzo eskirgan (' . abs($hozir - $vaqt) . ' soniya)';
}
// 3. MAC ni doimiy vaqtda solishtiramiz
$kutilgan = hash_hmac('sha256', $this->asos($vaqt, $tana), $this->kalit);
if (!hash_equals($kutilgan, $qismlar['v1'])) {
return 'RAD: imzo mos kelmadi';
}
return 'QABUL';
}
}
$imzolovchi = new VebxukImzosi('whsec_maxfiy_kalit');
$tana = '{"hodisa":"tolov.otdi","summa":250000}';
$vaqt = 1735689600;
$sarlavha = $imzolovchi->imzola($tana, $vaqt);
echo 'Sarlavha: ', substr($sarlavha, 0, 40), '...', PHP_EOL, PHP_EOL;
// 1. To'g'ri so'rov
echo '1. togri: ', $imzolovchi->tekshir($tana, $sarlavha, $vaqt), PHP_EOL;
// 2. Tana o'zgartirilgan
$buzilgan = '{"hodisa":"tolov.otdi","summa":25000000}';
echo '2. tana ozgardi: ',
$imzolovchi->tekshir($buzilgan, $sarlavha, $vaqt), PHP_EOL;
// 3. Eski so'rov qayta yuborilgan
echo '3. eskirgan: ',
$imzolovchi->tekshir($tana, $sarlavha, $vaqt + 3600), PHP_EOL;
// 4. Boshqa kalit bilan imzolangan
$boshqa = new VebxukImzosi('boshqa_kalit');
echo '4. boshqa kalit: ',
$imzolovchi->tekshir($tana, $boshqa->imzola($tana, $vaqt), $vaqt), PHP_EOL;
// 5. Buzuq sarlavha
echo '5. buzuq shakl: ',
$imzolovchi->tekshir($tana, 'abcdef', $vaqt), PHP_EOL;
Sarlavha: t=1735689600,v1=a993bcdf5bc176b9e8d194f9...
1. togri: QABUL
2. tana ozgardi: RAD: imzo mos kelmadi
3. eskirgan: RAD: imzo eskirgan (3600 soniya)
4. boshqa kalit: RAD: imzo mos kelmadi
5. buzuq shakl: RAD: sarlavha shakli notogri
Diqqat qiling: vaqt imzolanadigan matnning bir qismi
($vaqt . '.' . $tana), shunchaki sarlavhada emas.
Agar vaqt imzolanmagan bo'lsa, hujumchi uni o'zgartirib, eski so'rovni qayta yuborishi mumkin edi.
Bu takroriy uzatish hujumi (replay attack) dan himoya. Uning uch qismi bor:
| Qism | Vazifasi |
|---|---|
| Vaqt imzo ichida | Vaqtni o'zgartirib bo'lmaydi |
| Vaqt oynasi (5 daqiqa) | Eski so'rov rad etiladi |
| Serverlar vaqti sinxron | NTP ishlashi kerak |
Ba'zi tizimlar qo'shimcha ravishda bir marta ishlatiladigan
identifikator (nonce) ni ham saqlaydi va takrorlangan
so'rovni rad etadi. Bu ishonchliroq, lekin xotira talab
qiladi.
Nimani imzolash - eng nozik savol #
$kalit = 'test-kalit';
// Bir xil ma'lumot, turli JSON yozuvi
$a = '{"summa":100,"valyuta":"UZS"}';
$b = '{"valyuta":"UZS","summa":100}';
$c = '{"summa": 100, "valyuta": "UZS"}';
foreach (['a' => $a, 'b' => $b, 'c' => $c] as $nom => $json) {
printf('%s: %s%s', $nom,
substr(hash_hmac('sha256', $json, $kalit), 0, 24), PHP_EOL);
}
echo PHP_EOL, 'uchalasi bir xil malumot, lekin MAC lar har xil', PHP_EOL;
// Xuddi shu ma'lumot PHP massivi sifatida
foreach ([$a, $b, $c] as $json) {
$massiv = json_decode($json, true);
echo 'dekod: ', json_encode($massiv), PHP_EOL;
}
a: fc5ea00f75f7d9c9bafef608
b: 3da289572dea3dd4f3aa0f54
c: ab573f8fa9f04efb9491ab0c
uchalasi bir xil malumot, lekin MAC lar har xil
dekod: {"summa":100,"valyuta":"UZS"}
dekod: {"valyuta":"UZS","summa":100}
dekod: {"summa":100,"valyuta":"UZS"}
Yuqoridagi natija muhim saboq beradi: JSON ni qayta kodlash MAC ni buzadi.
Shuning uchun vebxuk qabul qilganda:
| To'g'ri | Noto'g'ri |
|---|---|
file_get_contents('php://input') xom tanasini imzolash | json_decode qilib, keyin qayta json_encode qilish |
| Framework xom tanani saqlagan bo'lsa - o'shani olish | $_POST massividan qayta qurish |
Yana bir tuzoq: ba'zi freymvorklar tanani avtomatik tahlil qiladi va xom holini tashlab yuboradi. Bunday holda middleware ni imzo tekshiruvidan keyin ishlatish kerak.
Umumiy qoida: imzo aynan uzatilgan baytlar ustidan hisoblanadi. Har qanday qayta ishlash imzoni buzadi.
MAC va raqamli imzo farqi #
$jadval = [
['Kalit', 'Bitta umumiy kalit', 'Juft: maxfiy + ochiq'],
['Kim yarata oladi', 'Ikkala tomon ham', 'Faqat maxfiy kalit egasi'],
['Kim tekshira oladi', 'Ikkala tomon ham', 'HAR KIM (ochiq kalit bilan)'],
['Rad eta olmaslik', 'YOQ', 'HA'],
['Tezlik', 'Juda tez', 'Ancha sekin'],
['Olcham', '32 bayt', '64-256 bayt'],
];
printf("%-20s %-22s %s%s", '', 'HMAC', 'Raqamli imzo', PHP_EOL);
echo str_repeat('-', 72), PHP_EOL;
foreach ($jadval as $q) {
printf("%-20s %-22s %s%s", ...[...$q, PHP_EOL]);
}
HMAC Raqamli imzo
------------------------------------------------------------------------
Kalit Bitta umumiy kalit Juft: maxfiy + ochiq
Kim yarata oladi Ikkala tomon ham Faqat maxfiy kalit egasi
Kim tekshira oladi Ikkala tomon ham HAR KIM (ochiq kalit bilan)
Rad eta olmaslik YOQ HA
Tezlik Juda tez Ancha sekin
Olcham 32 bayt 64-256 bayt
HMAC kaliti ikkala tomonda bor. Demak qabul qiluvchi ham xuddi shunday MAC yasay oladi.
Sud oldida: "Bu xabarni siz yubordingiz" degan da'voga javob - "Yo'q, uni siz o'zingiz yasagansiz, kalit sizda ham bor edi".
Raqamli imzoda bunday emas: maxfiy kalit faqat imzolovchida. Shuning uchun u rad eta olmaslikni beradi.
| Vazifa | Qaysi biri |
|---|---|
| API so'rovlarini imzolash (ikki tomon ham sizniki) | HMAC |
| Vebxuk (provayder ↔ siz) | HMAC |
| Sessiya kukisi | HMAC |
| Dasturiy ta'minot relizi | Raqamli imzo |
| Hujjatni imzolash | Raqamli imzo |
| Sertifikat | Raqamli imzo |
Raqamli imzolar 11-bo'limda ko'riladi.
Kalit uzunligi va qisqartirish #
$xabar = 'test';
// Kalit uzunligi
foreach ([8, 16, 32, 64] as $n) {
$kalit = str_repeat('k', $n);
printf('kalit %2d bayt -> MAC %d bayt%s', $n,
strlen(hash_hmac('sha256', $xabar, $kalit, true)), PHP_EOL);
}
echo PHP_EOL;
// MAC ni qisqartirish - ba'zan kerak bo'ladi
$toliq = hash_hmac('sha256', $xabar, 'kalit', true);
foreach ([32, 16, 8] as $n) {
printf('%2d bayt (%3d bit): %s%s', $n, $n * 8,
bin2hex(substr($toliq, 0, $n)), PHP_EOL);
}
kalit 8 bayt -> MAC 32 bayt
kalit 16 bayt -> MAC 32 bayt
kalit 32 bayt -> MAC 32 bayt
kalit 64 bayt -> MAC 32 bayt
32 bayt (256 bit): 120f1900d6ed2f40947f98cdc1b1712a0ae83671bf9018f223a2cfce73721c93
16 bayt (128 bit): 120f1900d6ed2f40947f98cdc1b1712a
8 bayt ( 64 bit): 120f1900d6ed2f40
| Parametr | Tavsiya |
|---|---|
| Kalit uzunligi | Kamida 32 bayt (random_bytes(32)) |
| Kalit manbai | random_bytes, parol emas |
| MAC uzunligi | To'liq 32 bayt |
| Qisqartirish | Faqat 16 baytdan (128 bit) pastga tushmang |
HMAC kaliti xesh blok o'lchamidan (SHA-256 uchun 64 bayt) uzun bo'lsa, u avval xeshlanadi - ya'ni 100 baytlik kalit 32 baytlik kalitdan kuchliroq emas.
Qisqartirish faqat joy tanqis bo'lganda (masalan QR kod, SMS kodi) qilinadi. 128 bitdan past - xavfli.
- Xabar va uning xeshini yuboring, keyin ikkalasini ham o'zgartiring.
- Qabul qiluvchi buni sezmasligini ko'rsating.
- Xuddi shu xabarni HMAC bilan imzolang va o'zgartirishga urinib ko'ring.
- HMAC ni qo'lda (ipad/opad bilan) hisoblang va
hash_hmacbilan solishtiring. - Vebxuk imzolovchi sinf yozing.
- Vaqt belgisini imzo ichiga kiriting va eskirgan so'rovni rad eting.
- Vaqtni imzodan chiqaring - qanday hujum mumkin bo'lib qoldi?
- Bir xil JSON ni turli tartibda yozib, MAC lari farq qilishini ko'ring.
hash_equalso'rniga===qo'ying va nima uchun bu xato ekanini yozing.- HMAC va raqamli imzo farqini uch qatorda tushuntiring.
Xulosa #
- Xesh ma'lumot o'zgarganini aniqlaydi, lekin kim o'zgartirganini aytmaydi.
- Hujumchi xabarni ham, uning xeshini ham qayta hisoblab qo'yadi.
- HMAC maxfiy kalit talab qiladi - kalitsiz to'g'ri kod hosil qilib bo'lmaydi.
- HMAC kalitni ikki qatlamda ishlatadi, shuning uchun uzunlikni kengaytirish ishlamaydi.
- MAC butunlik va autentiklik beradi, lekin maxfiylik bermaydi - xabar ochiq ketadi.
- Vebxuk imzosida vaqt belgisi imzo ichida bo'lishi shart.
- Vaqt oynasi (odatda 5 daqiqa) takroriy uzatishdan himoya qiladi.
- Imzo xom baytlar ustidan hisoblanadi - JSON ni qayta kodlash MAC ni buzadi.
- MAC ni solishtirish doim
hash_equalsbilan. - HMAC rad eta olmaslikni bermaydi - buning uchun raqamli imzo kerak.
Keyingi bo'limda paroldan va tasodifiy manbadan kalit hosil qilish bilan tanishamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.