5-bo‘lim

HMAC va xabar butunligi

Xabar o'zgartirilmaganini va kimdan kelganini isbotlash - vebxuk imzolari misolida.

🕑 13 daqiqa o‘qish 📄 943 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Muammo: yolg'iz xesh yetarli emas
  2. Yechim: HMAC
  3. HMAC ning ichki tuzilishi
  4. Amaliy misol: vebxuk imzosi
  5. Nimani imzolash - eng nozik savol
  6. MAC va raqamli imzo farqi
  7. Kalit uzunligi va qisqartirish
  8. Xulosa

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 #

PHP
function korsat(string $nom, string $qiymat): void
{
    printf("%-12s %s%s", $nom . ':', $qiymat, PHP_EOL);
}
PHP
// 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;
Natija
xabar:       foydalanuvchi=oddiy&rol=user
xesh:        a63aa56ffc56eec02b4d569034d2873e...

hujumchi ozgartirgandan keyin:
xabar:       foydalanuvchi=oddiy&rol=admin
xesh:        70a03c26b55edcb902a2881b0756c44c...

tekshiruv otdimi: HA - ALDANDI
Xesh sirni talab qilmaydi

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 himoyalaydiNimadan himoyalamaydi
Diskdagi xatolikAtaylab o'zgartirish
Tarmoqda buzilgan baytYo'ldagi hujumchi
Yarim yuklangan faylSoxta yuboruvchi

Autentiklik uchun sir kerak - va uni bilmagan odam kodni hisoblay olmasligi kerak.

Yechim: HMAC #

PHP
$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));
Natija
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 - xabar bilan birga uning "muhri" Yuboruvchi xabar + maxfiy kalit hash_hmac() → MAC xabar + MAC Ishonchsiz kanal hujumchi xabarni o'zgartirishi mumkin lekin kalit unda YO'Q Qabul qiluvchi kelgan xabar + o'sha kalit → o'z MAC ini hisoblaydi hash_equals() MAC nima kafolatlaydi Butunlik - xabar o'zgartirilmagan Autentiklik - uni kalitni bilgan tomon yuborgan ✗ Maxfiylik - xabar OCHIQ ketadi ✗ Rad eta olmaslik - kalit ikkalasida ham bor Nima uchun oddiy hash($kalit . $xabar) emas HMAC kalitni ikki marta ishlatadi: ichki va tashqi qatlamda. Bu 3-bo'limdagi uzunlikni kengaytirish hujumini imkonsiz qiladi.
HMAC - kalit talab qiladigan xesh; kalitsiz uni hisoblab bo'lmaydi

HMAC ning ichki tuzilishi #

PHP
// 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));
Natija
qo'lda: ebda4604c9799481b55bfccea84d81da...
tayyor: ebda4604c9799481b55bfccea84d81da...
bool(true)
Ikki qatlam nima uchun kerak

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 #

PHP
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';
    }
}
PHP
$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;
Natija
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
Vaqt belgisi nima uchun imzo ichida

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:

QismVazifasi
Vaqt imzo ichidaVaqtni o'zgartirib bo'lmaydi
Vaqt oynasi (5 daqiqa)Eski so'rov rad etiladi
Serverlar vaqti sinxronNTP 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 #

PHP
$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;
}
Natija
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"}
Xom baytlarni imzolang, obyektni emas

Yuqoridagi natija muhim saboq beradi: JSON ni qayta kodlash MAC ni buzadi.

Shuning uchun vebxuk qabul qilganda:

To'g'riNoto'g'ri
file_get_contents('php://input') xom tanasini imzolashjson_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 #

PHP
$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]);
}
Natija
                     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
"Rad eta olmaslik" nima uchun HMAC da yo'q

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.

VazifaQaysi biri
API so'rovlarini imzolash (ikki tomon ham sizniki)HMAC
Vebxuk (provayder ↔ siz)HMAC
Sessiya kukisiHMAC
Dasturiy ta'minot reliziRaqamli imzo
Hujjatni imzolashRaqamli imzo
SertifikatRaqamli imzo

Raqamli imzolar 11-bo'limda ko'riladi.

Kalit uzunligi va qisqartirish #

PHP
$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);
}
Natija
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
Kalit va MAC uzunligi
ParametrTavsiya
Kalit uzunligiKamida 32 bayt (random_bytes(32))
Kalit manbairandom_bytes, parol emas
MAC uzunligiTo'liq 32 bayt
QisqartirishFaqat 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.

Amaliy topshiriq
  1. Xabar va uning xeshini yuboring, keyin ikkalasini ham o'zgartiring.
  2. Qabul qiluvchi buni sezmasligini ko'rsating.
  3. Xuddi shu xabarni HMAC bilan imzolang va o'zgartirishga urinib ko'ring.
  4. HMAC ni qo'lda (ipad/opad bilan) hisoblang va hash_hmac bilan solishtiring.
  5. Vebxuk imzolovchi sinf yozing.
  6. Vaqt belgisini imzo ichiga kiriting va eskirgan so'rovni rad eting.
  7. Vaqtni imzodan chiqaring - qanday hujum mumkin bo'lib qoldi?
  8. Bir xil JSON ni turli tartibda yozib, MAC lari farq qilishini ko'ring.
  9. hash_equals o'rniga === qo'ying va nima uchun bu xato ekanini yozing.
  10. 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_equals bilan.
  • HMAC rad eta olmaslikni bermaydi - buning uchun raqamli imzo kerak.

Keyingi bo'limda paroldan va tasodifiy manbadan kalit hosil qilish bilan tanishamiz.

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.