7-bo‘lim

Simmetrik shifrlash va rejimlar

AES, blok rejimlari va nima uchun ECB hech qachon ishlatilmasligi kerak.

🕑 15 daqiqa o‘qish 📄 1 047 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Blok shifri nima
  2. ECB - eng oddiy va eng xavfli rejim
  3. CBC - bloklarni bog'lash
  4. CBC ning jiddiy kamchiligi: o'zgartirish sezilmaydi
  5. To'ldirish (padding)
  6. CTR - blok shifrini oqim shifriga aylantirish
  7. Rejimlarni solishtirish
  8. Xulosa

Simmetrik shifrlash - shifrlash va ochish uchun bitta kalit ishlatiladigan usul.

Eng keng tarqalgani - AES. Lekin AES ning o'zi yetarli emas: uni qaysi rejimda ishlatish natijani butunlay o'zgartiradi.

Blok shifri nima #

PHP
function bloklar(string $baytlar, int $olcham = 16): string
{
    $natija = [];
    foreach (str_split(bin2hex($baytlar), $olcham * 2) as $i => $b) {
        $natija[] = sprintf('  %d: %s', $i, $b);
    }
    return implode(PHP_EOL, $natija);
}
PHP
echo 'AES blok o\'lchami: ',
    openssl_cipher_iv_length('aes-256-cbc'), ' bayt', PHP_EOL;

foreach (['aes-128-cbc', 'aes-192-cbc', 'aes-256-cbc'] as $shifr) {
    // kalit uzunligini nomidan olamiz
    $bit = (int) explode('-', $shifr)[1];
    printf('%-12s kalit: %2d bayt (%d bit)%s', $shifr, $bit / 8, $bit, PHP_EOL);
}
Natija
AES blok o'lchami: 16 bayt
aes-128-cbc  kalit: 16 bayt (128 bit)
aes-192-cbc  kalit: 24 bayt (192 bit)
aes-256-cbc  kalit: 32 bayt (256 bit)
AES nima qiladi

AES 16 baytlik blokni 16 baytlik blokka aylantiradi. U faqat shu ishni qiladi.

XususiyatQiymat
Blok o'lchami16 bayt (128 bit) - doimo
Kalit o'lchami16, 24 yoki 32 bayt
Chiqish o'lchamiKirish bilan bir xil

Savol tug'iladi: 16 baytdan uzun ma'lumotni qanday shifrlash kerak?

Javob - rejim (mode of operation). U bloklarni qanday bog'lashni belgilaydi. Va aynan shu yerda eng ko'p xato qilinadi.

ECB - eng oddiy va eng xavfli rejim #

PHP
$kalit = hex2bin(str_repeat('11', 32));

// Ataylab takrorlanuvchi ma'lumot
$matn = str_repeat('AAAABBBBCCCCDDDD', 4);   // 4 ta bir xil blok

echo 'ochiq matn (4 blok, hammasi bir xil):', PHP_EOL;
echo bloklar($matn), PHP_EOL, PHP_EOL;

$shifr = openssl_encrypt($matn, 'aes-256-ecb', $kalit,
    OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);

echo 'ECB bilan shifrlangan:', PHP_EOL;
echo bloklar($shifr), PHP_EOL;
Natija
ochiq matn (4 blok, hammasi bir xil):
  0: 41414141424242424343434344444444
  1: 41414141424242424343434344444444
  2: 41414141424242424343434344444444
  3: 41414141424242424343434344444444

ECB bilan shifrlangan:
  0: a0aaf87de1cb2896d7bd3c2d99be11c9
  1: a0aaf87de1cb2896d7bd3c2d99be11c9
  2: a0aaf87de1cb2896d7bd3c2d99be11c9
  3: a0aaf87de1cb2896d7bd3c2d99be11c9
ECB naqshni saqlab qoladi

Shifrmatndagi to'rtala blok ham bir xil.

Sabab: ECB har blokni mustaqil shifrlaydi. Bir xil ochiq blok - har doim bir xil shifrblok.

Bu shuni anglatadi: hujumchi shifrni ochmasdan turib ma'lumot tuzilishini ko'radi.

Buni yaqqolroq ko'rsatamiz - oddiy "rasm" bilan:

PHP
$kalit = hex2bin(str_repeat('11', 32));

// 8x8 "rasm": 1 - qora nuqta, 0 - oq
$rasm = [
    '0000000000000000', '0011111111110000', '0011000000110000',
    '0011000000110000', '0011111111110000', '0011000000110000',
    '0011000000110000', '0000000000000000',
];

echo 'ASL "RASM":', PHP_EOL;
foreach ($rasm as $q) {
    echo '  ', strtr($q, ['0' => '.', '1' => '#']), PHP_EOL;
}

// Har qatorni alohida blok sifatida ECB bilan shifrlaymiz
echo PHP_EOL, 'ECB dan KEYIN (har qator - bitta blok):', PHP_EOL;
foreach ($rasm as $q) {
    $s = openssl_encrypt($q, 'aes-256-ecb', $kalit,
        OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    echo '  ', substr(bin2hex($s), 0, 32), PHP_EOL;
}
Natija
ASL "RASM":
  ................
  ..##########....
  ..##......##....
  ..##......##....
  ..##########....
  ..##......##....
  ..##......##....
  ................

ECB dan KEYIN (har qator - bitta blok):
  c49df3f75ca6d86094ded52d5def0e2a
  9f792a648eefcb768b160a072aa28af5
  136727c6b09d854eeed0afa396896fee
  136727c6b09d854eeed0afa396896fee
  9f792a648eefcb768b160a072aa28af5
  136727c6b09d854eeed0afa396896fee
  136727c6b09d854eeed0afa396896fee
  c49df3f75ca6d86094ded52d5def0e2a
Naqsh saqlanib qoldi

Chiqishga diqqat bilan qarang: bir xil qatorlar bir xil shifrmatn berdi.

  • 1-va 8-qator (0000...) - bir xil
  • 3-, 4-, 6-, 7-qatorlar (0011000000110000) - bir xil
  • 2-va 5-qator (0011111111110000) - bir xil

Ya'ni "rasm" ning tuzilishi shifrlangandan keyin ham o'qiladi.

Aynan shu sabab bilan ECB rejimida shifrlangan tasvir mashhur misolga aylangan: pingvin rasmi shifrlangandan keyin ham tanib olinadi.

ECB nima yashiradiNima yashirmaydi
Har blokning aniq mazmuniBloklar takrorlanishini
Baytlarning qiymatiMa'lumot tuzilishini
Uzunlikni
Naqshlarni

Xulosa: ECB ni hech qachon ishlatmang. Undan foydalanadigan yagona to'g'ri holat - bitta, tasodifiy, takrorlanmaydigan blokni shifrlash.

CBC - bloklarni bog'lash #

PHP
$kalit = hex2bin(str_repeat('11', 32));
$iv = hex2bin(str_repeat('22', 16));

$matn = str_repeat('AAAABBBBCCCCDDDD', 4);

$shifr = openssl_encrypt($matn, 'aes-256-cbc', $kalit,
    OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);

echo 'CBC bilan (bir xil ochiq bloklar):', PHP_EOL;
echo bloklar($shifr), PHP_EOL;

echo PHP_EOL, 'endi bloklar HAR XIL', PHP_EOL;
Natija
CBC bilan (bir xil ochiq bloklar):
  0: 918973ccd319a41aa72b4c929945d49b
  1: f32d20b5ef53af71374414749e5824cd
  2: bb1d398cfb04a7f9d62f742171562dac
  3: 988295171fd30a18b9a63fb1b94fc0f7

endi bloklar HAR XIL

CBC har blokni shifrlashdan oldin oldingi shifrblok bilan XOR qiladi. Birinchi blok uchun esa IV (initialization vector) ishlatiladi.

PHP
$kalit = hex2bin(str_repeat('11', 32));
$matn = 'Bir xil xabar!!!';

// Bir xil matn, TURLI IV
foreach (['33', '44', '55'] as $bayt) {
    $iv = hex2bin(str_repeat($bayt, 16));
    $s = openssl_encrypt($matn, 'aes-256-cbc', $kalit,
        OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
    printf('IV %s...: %s%s', $bayt . $bayt, bin2hex($s), PHP_EOL);
}
Natija
IV 3333...: a7b0f8cbb0eac7bb47324e16053e3eaf
IV 4444...: f2be64d906943acb2ee5854b41e0e925
IV 5555...: 2a10c4a31c42774ae30698794ca6d08a
ECB va CBC - bloklar qanday bog'lanadi ECB - har blok mustaqil blok 1 blok 2 blok 1 AES AES AES c1 c2 c1 Bir xil blok → bir xil shifr naqsh oshkor bo'ladi CBC - zanjir blok 1 blok 2 blok 1 IV AES AES AES c1 c2 c3 Bir xil blok → HAR XIL shifr CBC yaxshiroq, lekin YETARLI EMAS CBC maxfiylik beradi, lekin butunlikni tekshirmaydi. Hujumchi shifrmatnni o'zgartira oladi va ochish paytida buni hech kim sezmaydi. Yechim: autentifikatsiyalangan shifrlash AES-GCM yoki ChaCha20-Poly1305 - shifrlash va butunlik bir amalda (8-bo'lim). CBC dan foydalanish kerak bo'lsa - ustiga majburiy HMAC qo'shiladi (encrypt-then-MAC).
ECB naqshni saqlaydi, CBC yashiradi - lekin ikkalasi ham butunlikni tekshirmaydi

CBC ning jiddiy kamchiligi: o'zgartirish sezilmaydi #

PHP
$kalit = hex2bin(str_repeat('11', 32));
$iv = hex2bin(str_repeat('22', 16));

// Ikki blokli xabar
$matn = 'AAAAAAAAAAAAAAAA' . 'rol=user;id=1234';

$shifr = openssl_encrypt($matn, 'aes-256-cbc', $kalit,
    OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);

echo 'asl matn:  ', $matn, PHP_EOL;
echo 'ochilgan:  ', openssl_decrypt($shifr, 'aes-256-cbc', $kalit,
    OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv), PHP_EOL;

// Hujumchi BIRINCHI blokning bir baytini o'zgartiradi
$buzilgan = $shifr;
$buzilgan[5] = chr(ord($buzilgan[5]) ^ 0x04);

$natija = openssl_decrypt($buzilgan, 'aes-256-cbc', $kalit,
    OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);

echo PHP_EOL, 'shifrmatn ozgartirilgandan keyin:', PHP_EOL;
echo 'xato berdimi: ', $natija === false ? 'ha' : 'YOQ', PHP_EOL;
echo '1-blok:    ', bin2hex(substr($natija, 0, 16)), PHP_EOL;
echo '2-blok:    ', substr($natija, 16), PHP_EOL;
Natija
asl matn:  AAAAAAAAAAAAAAAArol=user;id=1234
ochilgan:  AAAAAAAAAAAAAAAArol=user;id=1234

shifrmatn ozgartirilgandan keyin:
xato berdimi: YOQ
1-blok:    9ab2b2f2017f69d7a740c1f73fdff2d1
2-blok:    rol=uwer;id=1234
Ochish xato bermadi - va ikkinchi blok o'zgardi

Ikki narsa sodir bo'ldi:

  1. Birinchi blok axlatga aylandi (kutilgan natija).
  2. Ikkinchi blok aniq boshqarilgan tarzda o'zgardi: rol=user -> rol=uwer. Biz shifrmatnning 5-baytini 0x04 bilan XOR qildik va ochiq matnning aynan 5-bayti o'sha qiymatga o'zgardi (s = 0x73 -> w = 0x77).

CBC da ochish shunday ishlaydi:

KOD
ochiq_blok[i] = AES_ochish(shifr_blok[i]) XOR shifr_blok[i-1]

Ya'ni shifr_blok[i-1] dagi har bir bit ochiq_blok[i] dagi o'sha o'rindagi bitni to'g'ridan-to'g'ri o'zgartiradi.

Hujumchi bitta blokni qurbon qilib, keyingi blokni tanlagan tarzda o'zgartira oladi. Masalan rol=user ni rol=admin ga aylantirish.

Bu moslashuvchanlik (malleability) muammosi. U CBC ning kamchiligi emas, balki butunlik tekshiruvi yo'qligining oqibati.

Eng muhimi: openssl_decrypt false qaytarmadi. Kod buzilgan ma'lumotni normal deb qabul qildi.

To'ldirish (padding) #

PHP
$kalit = hex2bin(str_repeat('11', 32));
$iv = hex2bin(str_repeat('22', 16));

foreach (['a', 'salom', str_repeat('x', 15), str_repeat('x', 16)] as $m) {
    // OPENSSL_ZERO_PADDING SIZ - PKCS#7 to'ldirish ishlatiladi
    $s = openssl_encrypt($m, 'aes-256-cbc', $kalit, OPENSSL_RAW_DATA, $iv);
    printf('%2d bayt matn -> %2d bayt shifr%s',
        strlen($m), strlen($s), PHP_EOL);
}
Natija
 1 bayt matn -> 16 bayt shifr
 5 bayt matn -> 16 bayt shifr
15 bayt matn -> 16 bayt shifr
16 bayt matn -> 32 bayt shifr
PKCS#7 to'ldirish

Blok shifri to'liq bloklar bilan ishlaydi, shuning uchun oxirgi blok to'ldiriladi.

PKCS#7 qoidasi: nechta bayt yetishmasa, o'sha qiymatdagi shuncha bayt qo'shiladi.

Matn uzunligiQo'shiladiTo'liq uzunlik
15 bayt01 × 116
14 bayt02 × 216
16 bayt10 × 1632

Oxirgi qatorga e'tibor bering: matn aynan blok uzunligida bo'lsa ham, butun bir blok qo'shiladi. Aks holda ochishda to'ldirish bor-yo'qligini bilib bo'lmasdi.

Tarixiy jihatdan to'ldirish jiddiy zaiflik manbai bo'lgan (padding oracle): server "to'ldirish noto'g'ri" degan xato bersa, hujumchi shu javobga qarab shifrni bayt-bayt ochishi mumkin edi.

AEAD rejimlari (8-bo'lim) bu muammoni butunlay yo'q qiladi: ular oqim shifridan foydalanadi va to'ldirish umuman kerak emas.

CTR - blok shifrini oqim shifriga aylantirish #

PHP
$kalit = hex2bin(str_repeat('11', 32));
$nonce = hex2bin(str_repeat('33', 16));

foreach (['a', 'salom', str_repeat('x', 100)] as $m) {
    $s = openssl_encrypt($m, 'aes-256-ctr', $kalit, OPENSSL_RAW_DATA, $nonce);
    printf('%3d bayt matn -> %3d bayt shifr%s',
        strlen($m), strlen($s), PHP_EOL);
}

echo PHP_EOL, 'CTR da toldirish YOQ - uzunlik bir xil', PHP_EOL;
Natija
  1 bayt matn ->   1 bayt shifr
  5 bayt matn ->   5 bayt shifr
100 bayt matn -> 100 bayt shifr

CTR da toldirish YOQ - uzunlik bir xil

Rejimlarni solishtirish #

RejimNaqsh yashiriladimiTo'ldirishButunlikTavsiya
ECBYo'qKerakYo'qHech qachon
CBCHa (IV bilan)KerakYo'qFaqat HMAC bilan
CTRHaKerak emasYo'qFaqat HMAC bilan
GCMHaKerak emasHaHa
ChaCha20-Poly1305HaKerak emasHaHa
PHP
$mavjud = openssl_get_cipher_methods();

foreach (['aes-256-ecb', 'aes-256-cbc', 'aes-256-ctr',
          'aes-256-gcm', 'chacha20-poly1305'] as $c) {
    printf('%-20s %s%s', $c,
        in_array($c, $mavjud, true) ? 'mavjud' : 'yoq', PHP_EOL);
}
Natija
aes-256-ecb          mavjud
aes-256-cbc          mavjud
aes-256-ctr          mavjud
aes-256-gcm          mavjud
chacha20-poly1305    mavjud
Amaliy xulosa

Yangi kod yozayotgan bo'lsangiz, tanlov oddiy:

VaziyatTanlov
Umumiy holatAES-256-GCM
Uskunada AES tezlatgichi yo'qChaCha20-Poly1305
Juda uzun oqim (terabaytlar)Bo'laklarga bo'lib AEAD
Eski tizim bilan moslikCBC + HMAC (encrypt-then-MAC)

ECB va "yalang'och" CBC ni yangi kodda umuman ishlatmang.

Keyingi bo'lim to'liq AEAD ga bag'ishlangan.

Amaliy topshiriq
  1. AES blok o'lchamini va uch xil kalit uzunligini chiqaring.
  2. Takrorlanuvchi matnni ECB bilan shifrlang va bloklarni solishtiring.
  3. "Rasm" misolini takrorlang - qaysi qatorlar bir xil chiqdi?
  4. Xuddi shu matnni CBC bilan shifrlang va farqni ko'ring.
  5. Bir xil matnni uch xil IV bilan shifrlab, natijalarni solishtiring.
  6. CBC shifrmatnining bir baytini o'zgartiring va ochib ko'ring.
  7. Ochish xato berdimi? Ikkinchi blok qanday o'zgardi?
  8. 15, 16 va 17 baytlik matnlar uchun shifr uzunligini o'lchang.
  9. Xuddi shu matnlarni CTR bilan shifrlang - uzunlik o'zgardimi?
  10. Qaysi rejimni qachon ishlatish kerakligini jadval qilib yozing.

Xulosa #

  • AES 16 baytlik blokni shifrlaydi; undan uzun ma'lumot uchun rejim kerak.
  • ECB har blokni mustaqil shifrlaydi va naqshni saqlab qoladi.
  • Bir xil ochiq blok ECB da har doim bir xil shifrblok beradi - tuzilma oshkor bo'ladi.
  • ECB ni hech qachon ishlatmang.
  • CBC har blokni oldingi shifrblok bilan XOR qiladi, birinchisi uchun IV ishlatiladi.
  • Bir xil matn turli IV bilan butunlay boshqa shifrmatn beradi.
  • CBC butunlikni tekshirmaydi: o'zgartirilgan shifrmatn xato bermay ochiladi.
  • Hujumchi bitta blokni qurbon qilib, keyingisini boshqarib o'zgartira oladi.
  • PKCS#7 to'ldirish: matn blok uzunligiga teng bo'lsa ham butun blok qo'shiladi.
  • Yangi kodda AES-256-GCM yoki ChaCha20-Poly1305 ishlating.

Keyingi bo'limda autentifikatsiyalangan shifrlash (AEAD) 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.