11-bo‘lim
Raqamli imzolar
Kim imzolaganini isbotlash - va tekshirish kodidagi eng ko'p uchraydigan xato.
Ushbu bo‘lim mundarijasi
Raqamli imzo - maxfiy kalit bilan yaratiladigan, ochiq kalit bilan tekshiriladigan "muhr".
HMAC dan farqi: tekshirish uchun maxfiy kalit kerak emas. Demak imzoni har kim tekshira oladi, lekin faqat bitta odam yarata oladi.
Imzolash va tekshirish #
// DIQQAT: darslik uchun ochiq e'lon qilingan kalit. Real tizimda ishlatmang.
const DEMO_MAXFIY = <<<'PEM'
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDAMaCT68asinZp
/aYbrjiNHhKSbFXOLpXpcwlZYSejSoVuSyLchWD55wE/P7zLt+ZKB34zrpwFNPfq
ICInvr5o2VwoUQ30XsGOPP680send61FKUtiqZp7kMG1R/56TJXT+Lrt/ls5kRGi
IFfcWY0KhaQ6+88nMX7WAv40ivQnhhuFTo/hLIMrbdLYpoJOgNzC4gKAuyhLVIfg
2yVFyKy50MtfBa2gv5Jmz1s9FAW38pRQIThLd2TEqMtxtqB6kk3Z7W+lzZDrvvw0
XGCK2P58jBRx9utNABb4wyMWGYbrXZY9bkt64Gx+ioTlZl8rGQuNLM0MT1DNCosh
gEb5PGHjAgMBAAECggEAVmQ4bSV3mclnrv8gcqLesrhI2t6tt0+DfXuDQVUFr0Xp
Mom9suiVjd/D1L1+zQfuEWrK1MpxQXns0qX9FGaOSq0h2tGMd0M8uAY8733D10hZ
b5Q/WaspUM/h8u3YuvrAApd011QVh4vpJDprpBXw60DlcMoN9O0vuV2BDPdFBppf
RId6Fz4NzO63WjTbTvbocwZSTPWur1rbiLFDTc5jbhOUCiLmIyKuLh4MheQa8PFS
y6CCA8YPzLWLXOXW5cDTrOTut0cr8J9fdinm1TuxjHlgxm7tWFEQZwJHOLVHNB4I
o5qBDDD7XwBnijiLSejmAc2OiCPHUPZZY52VkvvLmQKBgQDhuRC+UOgQO/7O4wWf
3hwjsxMWydThnK10+i4EakVEOOC3DtjbC/lv4zvrVdS8w1/gX2qyeZ4F/0UAL/Sc
31C9NDkaqhIGjF/AN4NEBp5d0+dJYR2hoJDb/j4vnGBx+Fa/cFSOj443ZnJB9PMp
OoGcRXKSTIXwQdSgq7tyKngCnwKBgQDZ+Ttn9LSlT8RZZpI2FQ8oObI/3cvaTvHI
eYoviwUmBVJlwj27JNAiYqfy4VLmXcxqbn+snkpYL50qPm+/SfRXcXIm6CngSLtS
be2wf4OyAuYvAvtoJb5zhimU+uVpF3HTBi8nPGGwK93XoRDVwSRsNN17ahdmrCqJ
nBwNNbb+PQKBgQCA07r2o/6OpW88bN2c7Itq5J2UeAUD7wQgq1jwNCbsVPoMAHFM
M31dmndsTURpp6hkypB+5GKIUbTz8mrNzbarBc9BpKc9DYjoj8ZfYNep/XMXslHT
GW0T4ftj+QAii/YWtOnNo6krfVDgzAisEzQ+GHTXgs0+3pOYT+tfsVVlSQKBgQCt
HcbSr4TxbkLRhidFkMAzAG/TTYJj5iMdR3o6fD0gj0V70CNbCMfNJ3Gf8tj2YTTC
qvP30tfU5iv6qw/xavkGofE5eVYY7Wro8qm+befYb3kFiWbGtoNmGP3ZdtLQ/m/G
W7kDfm9PjedpJGDMg9M7pVjqdb4UK1S1aijVTP4DOQKBgHMmAkzUb6XG2fNPJ3oi
dmcKb6VLKhAFtUiasNeo2EtN3F2jBbs7hLbbAqJEknhyqZttmZQA/azq6BYUUFuO
JaI72NPuT7d3qxciD1IrcwLXbGYbRiHbMvMNrS//FNFgdoNgLqIy6tu8AieLa769
bIqqSM4fDts4Kt1eSpsvjKYo
-----END PRIVATE KEY-----
PEM;
const DEMO_OCHIQ = <<<'PEM'
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwDGgk+vGrIp2af2mG644
jR4SkmxVzi6V6XMJWWEno0qFbksi3IVg+ecBPz+8y7fmSgd+M66cBTT36iAiJ76+
aNlcKFEN9F7Bjjz+vNLHp3etRSlLYqmae5DBtUf+ekyV0/i67f5bOZERoiBX3FmN
CoWkOvvPJzF+1gL+NIr0J4YbhU6P4SyDK23S2KaCToDcwuICgLsoS1SH4NslRcis
udDLXwWtoL+SZs9bPRQFt/KUUCE4S3dkxKjLcbagepJN2e1vpc2Q6778NFxgitj+
fIwUcfbrTQAW+MMjFhmG612WPW5LeuBsfoqE5WZfKxkLjSzNDE9QzQqLIYBG+Txh
4wIDAQAB
-----END PUBLIC KEY-----
PEM;
function imzola(string $xabar): string
{
$maxfiy = openssl_pkey_get_private(DEMO_MAXFIY);
if ($maxfiy === false) {
throw new RuntimeException('maxfiy kalit oqilmadi');
}
if (!openssl_sign($xabar, $imzo, $maxfiy, OPENSSL_ALGO_SHA256)) {
throw new RuntimeException('imzolash muvaffaqiyatsiz');
}
return $imzo;
}
$xabar = 'Reliz 2.4.0, sha256: 3b1f...';
$imzo = imzola($xabar);
printf("%-16s %d bayt%s", 'imzo uzunligi:', strlen($imzo), PHP_EOL);
printf("%-16s %s...%s", 'imzo boshi:', substr(bin2hex($imzo), 0, 32), PHP_EOL);
// RSA PKCS#1 v1.5 imzosi DETERMINISTIK - ikkinchi marta ham bir xil
$ikkinchi = imzola($xabar);
printf("%-16s %s%s", 'ikki imzo teng:',
hash_equals($imzo, $ikkinchi) ? 'ha' : 'yoq', PHP_EOL);
imzo uzunligi: 256 bayt
imzo boshi: 46ee07c63e8f0f51855d7e341a734105...
ikki imzo teng: ha
Tekshirish #
$xabar = 'Reliz 2.4.0, sha256: 3b1f...';
$imzo = imzola($xabar);
// 1. To'g'ri xabar va to'g'ri imzo
$natija = openssl_verify($xabar, $imzo, DEMO_OCHIQ, OPENSSL_ALGO_SHA256);
printf("%-22s %d%s", 'togri xabar:', $natija, PHP_EOL);
// 2. Xabar o'zgartirilgan
$natija = openssl_verify('Reliz 2.4.1, sha256: 3b1f...', $imzo,
DEMO_OCHIQ, OPENSSL_ALGO_SHA256);
printf("%-22s %d%s", 'ozgartirilgan xabar:', $natija, PHP_EOL);
// 3. Imzo o'zgartirilgan
$buzuq = $imzo;
$buzuq[0] = chr(ord($buzuq[0]) ^ 0x01);
$natija = openssl_verify($xabar, $buzuq, DEMO_OCHIQ, OPENSSL_ALGO_SHA256);
printf("%-22s %d%s", 'buzilgan imzo:', $natija, PHP_EOL);
// 4. Butunlay noto'g'ri imzo
$natija = openssl_verify($xabar, 'axlat', DEMO_OCHIQ, OPENSSL_ALGO_SHA256);
printf("%-22s %d%s", 'axlat imzo:', $natija, PHP_EOL);
togri xabar: 1
ozgartirilgan xabar: 0
buzilgan imzo: 0
axlat imzo: 0
openssl_verify uchta qiymat qaytaradiBu PHP dagi eng xavfli tuzoqlardan biri:
| Qaytadi | Ma'nosi |
|---|---|
1 | Imzo to'g'ri |
0 | Imzo noto'g'ri |
-1 | Xato yuz berdi (kalit o'qilmadi va h.k.) |
Endi diqqat qiling:
if (openssl_verify($x, $imzo, $kalit)) {
// KIRISHGA RUXSAT
}
Bu kod buzuq. -1 ham "rost" deb hisoblanadi, chunki
PHP da nolga teng bo'lmagan har qanday son rost.
Ya'ni kalit o'qilmagan holatda kod imzoni to'g'ri deb qabul qiladi.
To'g'ri yozilishi:
if (openssl_verify($x, $imzo, $kalit, OPENSSL_ALGO_SHA256) === 1) {
=== 1 - majburiy.
// Boshqa TURDAGI kalit bilan tekshirish - xato holati
const EC_OCHIQ = <<<'PEM'
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEotEY2TwZ3ViMr0eo5gvz2SLX49re
uaXUCsG/9XmYgdCQNRukuCAv9+vfRY8M5wwxf2vj+wOsj9ekJzXZgAK8aw==
-----END PUBLIC KEY-----
PEM;
$xabar = 'test';
$imzo = imzola($xabar); // RSA imzosi
// RSA imzosini EC kaliti bilan tekshiramiz
$natija = @openssl_verify($xabar, $imzo, EC_OCHIQ, OPENSSL_ALGO_SHA256);
printf("%-28s %s%s", 'natija:', var_export($natija, true), PHP_EOL);
printf("%-28s %s%s", 'if ($natija) ishlaydimi:',
$natija ? 'HA - IMZO QABUL QILINDI!' : 'yoq', PHP_EOL);
printf("%-28s %s%s", '$natija === 1 ishlaydimi:',
$natija === 1 ? 'ha' : 'yoq - TOGRI RAD ETILDI', PHP_EOL);
natija: -1
if ($natija) ishlaydimi: HA - IMZO QABUL QILINDI!
$natija === 1 ishlaydimi: yoq - TOGRI RAD ETILDI
-1 ning oqibatiRSA imzosi EC kaliti bilan tekshirildi. Bu xato holat - kalit turi imzoga mos kelmaydi.
openssl_verify -1 qaytardi. Va -1 PHP da rost.
Demak if (openssl_verify(...)) yozilgan kod bu imzoni
qabul qiladi - garchi u umuman tekshirilmagan bo'lsa
ham.
Bu shunchaki nazariy xavf emas: hujumchi tizimga o'z ochiq kalitini (boshqa turdagi) kiritishga erisha olsa, istalgan imzo "to'g'ri" bo'lib qoladi.
Shuning uchun === 1 yozing.
Amaliy misol: dasturiy ta'minot relizi #
final class RelizImzosi
{
public function imzola(string $fayl): array
{
$xesh = hash_file('sha256', $fayl);
$manifest = json_encode([
'fayl' => basename($fayl),
'hajm' => filesize($fayl),
'sha256' => $xesh,
'versiya' => '2.4.0',
], JSON_UNESCAPED_SLASHES);
// MANIFESTNI imzolaymiz, faylning o'zini emas
return [
'manifest' => $manifest,
'imzo' => base64_encode(imzola($manifest)),
];
}
public function tekshir(string $fayl, array $reliz): string
{
// 1. Imzo to'g'rimi
$natija = openssl_verify($reliz['manifest'],
base64_decode($reliz['imzo']), DEMO_OCHIQ, OPENSSL_ALGO_SHA256);
if ($natija !== 1) {
return $natija === -1
? 'RAD: tekshirishda xato'
: 'RAD: imzo notogri';
}
// 2. Manifestni endi ishonchli deb o'qiymiz
$m = json_decode($reliz['manifest'], true, 512, JSON_THROW_ON_ERROR);
// 3. Fayl manifestga mos keladimi
if (filesize($fayl) !== $m['hajm']) {
return 'RAD: hajm mos kelmadi';
}
if (!hash_equals($m['sha256'], hash_file('sha256', $fayl))) {
return 'RAD: fayl xeshi mos kelmadi';
}
return "QABUL: {$m['fayl']} v{$m['versiya']}";
}
}
$fayl = sys_get_temp_dir() . '/reliz-2.4.0.sh';
file_put_contents($fayl, "#!/bin/sh\necho 'dastur 2.4.0'\n");
$imzolovchi = new RelizImzosi();
$reliz = $imzolovchi->imzola($fayl);
echo 'manifest: ', $reliz['manifest'], PHP_EOL;
echo 'imzo: ', substr($reliz['imzo'], 0, 40), '...', PHP_EOL;
echo PHP_EOL;
// 1. Toza fayl
echo '1. ozgarishsiz: ', $imzolovchi->tekshir($fayl, $reliz), PHP_EOL;
// 2. Fayl o'zgartirilgan (zararli kod qo'shilgan)
file_put_contents($fayl, "#!/bin/sh\necho 'dastur 2.4.0'\n# qoshimcha\n");
echo '2. fayl ozgardi: ', $imzolovchi->tekshir($fayl, $reliz), PHP_EOL;
// 3. Manifest ham o'zgartirilgan (hujumchi xeshni yangiladi)
$soxta = $reliz;
$m = json_decode($soxta['manifest'], true);
$m['sha256'] = hash_file('sha256', $fayl);
$m['hajm'] = filesize($fayl);
$soxta['manifest'] = json_encode($m, JSON_UNESCAPED_SLASHES);
echo '3. manifest ham: ', $imzolovchi->tekshir($fayl, $soxta), PHP_EOL;
unlink($fayl);
manifest: {"fayl":"reliz-2.4.0.sh","hajm":30,"sha256":"98fedfa5cb178d56ab67ebbe64108bed4676e7301a75877a80d7c12a646703b0","versiya":"2.4.0"}
imzo: MCS7Che5Yd+Qpmkjx+RyK/GhJNsdnH5d3Y8qa34g...
1. ozgarishsiz: QABUL: reliz-2.4.0.sh v2.4.0
2. fayl ozgardi: RAD: fayl xeshi mos kelmadi
3. manifest ham: RAD: imzo notogri
Hujumchi faylni o'zgartirdi va manifestdagi xeshni ham yangiladi. Mantiqan hammasi mos keladi.
Lekin imzo mos kelmadi, chunki hujumchida maxfiy kalit yo'q.
Bu 5-bo'limdagi xesh muammosining yechimi: xesh yolg'iz o'zi hech narsani isbotlamaydi, chunki uni har kim qayta hisoblaydi. Imzo esa sirni talab qiladi.
Diqqat qiling: manifest imzolangan, faylning o'zi emas. Bu ataylab shunday:
| Foyda | Izoh |
|---|---|
| Imzolash tez | 200 baytlik manifest, 2 GB lik fayl emas |
| Metama'lumot ham himoyalangan | Versiya, hajm, nom |
| Bir imzo - ko'p fayl | Manifestda ro'yxat bo'lishi mumkin |
| Tekshirish bosqichma-bosqich | Avval imzo, keyin xesh |
Tartib muhim: avval imzoni tekshiring, keyin manifest mazmuniga ishoning. Teskarisi - ishonchsiz ma'lumotga tayanish demakdir.
Imzo algoritmini tanlash #
$xabar = 'test xabar';
$maxfiy = openssl_pkey_get_private(DEMO_MAXFIY);
foreach ([
'SHA-1' => OPENSSL_ALGO_SHA1,
'SHA-256' => OPENSSL_ALGO_SHA256,
'SHA-384' => OPENSSL_ALGO_SHA384,
'SHA-512' => OPENSSL_ALGO_SHA512,
] as $nom => $algo) {
openssl_sign($xabar, $imzo, $maxfiy, $algo);
printf('%-9s imzo %d bayt, tekshiruv: %d%s', $nom, strlen($imzo),
openssl_verify($xabar, $imzo, DEMO_OCHIQ, $algo), PHP_EOL);
}
SHA-1 imzo 256 bayt, tekshiruv: 1
SHA-256 imzo 256 bayt, tekshiruv: 1
SHA-384 imzo 256 bayt, tekshiruv: 1
SHA-512 imzo 256 bayt, tekshiruv: 1
RSA PKCS#1 imzosi ichida qaysi xesh ishlatilgani ko'rsatilgan.
Shuning uchun openssl_verify ga noto'g'ri algoritm bersangiz,
tekshirish 0 beradi.
Lekin bu himoya emas. Xavfli naqsh shunday ko'rinadi:
// XATO: algoritmni ISHONCHSIZ manbadan olish
$algo = $sorov['alg'];
openssl_verify($xabar, $imzo, $kalit, $algo);
Hujumchi alg ni o'zgartirib, zaifroq algoritmga
"tushirishi" mumkin.
To'g'ri yondashuv:
| Qoida | Sabab |
|---|---|
| Algoritm kodda qat'iy yozilsin | Hujumchi tanlay olmasin |
| Faqat ruxsat etilgan ro'yxat | match bilan tekshirish |
| SHA-1 rad etilsin | 3-bo'lim: buzilgan |
| Kalit turi ham tekshirilsin | RSA imzosi RSA kaliti bilan |
Bu algoritm chalkashligi (algorithm confusion) hujumi. Uning eng mashhur shakli JWT da uchraydi va 17-bo'limda batafsil ko'riladi.
Imzo va HMAC: qaysi biri kerak #
$jadval = [
['Kim yarata oladi', 'ikkala tomon', 'faqat kalit egasi'],
['Kim tekshira oladi', 'ikkala tomon', 'HAR KIM'],
['Rad eta olmaslik', 'yoq', 'HA'],
['Kalit tarqatish', 'xavfsiz kanal kerak', 'ochiq kalit erkin'],
['Tezlik', 'juda tez', 'sekin'],
['Olcham', '32 bayt', '64-256 bayt'],
];
printf("%-20s %-22s %s%s", '', 'HMAC', 'Raqamli imzo', PHP_EOL);
echo str_repeat('-', 62), PHP_EOL;
foreach ($jadval as $q) {
printf("%-20s %-22s %s%s", ...[...$q, PHP_EOL]);
}
echo PHP_EOL, 'Qoida: uchinchi tomon tekshirishi kerakmi?', PHP_EOL;
echo ' ha -> raqamli imzo', PHP_EOL;
echo ' yoq -> HMAC (tezroq va soddaroq)', PHP_EOL;
HMAC Raqamli imzo
--------------------------------------------------------------
Kim yarata oladi ikkala tomon faqat kalit egasi
Kim tekshira oladi ikkala tomon HAR KIM
Rad eta olmaslik yoq HA
Kalit tarqatish xavfsiz kanal kerak ochiq kalit erkin
Tezlik juda tez sekin
Olcham 32 bayt 64-256 bayt
Qoida: uchinchi tomon tekshirishi kerakmi?
ha -> raqamli imzo
yoq -> HMAC (tezroq va soddaroq)
- Xabarni imzolang va imzo uzunligini yozing.
- Xuddi shu xabarni ikki marta imzolang - imzolar bir xilmi?
- Xabarni o'zgartirib tekshiring - natija nima?
- Imzoning bir baytini buzib tekshiring.
openssl_verifyning uchta qaytish qiymatini sanab bering.if ($natija)vaif ($natija === 1)farqini ko'rsating.- Reliz manifestini imzolovchi sinf yozing.
- Faylni va manifestni ikkalasini ham o'zgartirib ko'ring - imzo o'tdimi?
- Nima uchun manifest imzolanadi, fayl emas - tushuntiring.
- Algoritmni foydalanuvchi kiritmasidan olish nima uchun xavfli ekanini yozing.
Xulosa #
- Raqamli imzo maxfiy kalit bilan yaratiladi, ochiq kalit bilan tekshiriladi.
- HMAC dan farqi: tekshirish uchun sir kerak emas - har kim tekshira oladi.
- Shu sababli imzo rad eta olmaslikni beradi.
- RSA PKCS#1 v1.5 imzosi deterministik - bir xil xabar bir xil imzo beradi.
openssl_verifyuchta qiymat qaytaradi:1,0va-1.if (openssl_verify(...))- buzuq kod:-1ham rost hisoblanadi.- Har doim
=== 1bilan tekshiring. - Katta fayllar uchun manifest imzolanadi: xesh, hajm, versiya.
- Avval imzoni tekshiring, keyin manifest mazmuniga ishoning.
- Imzo algoritmi kodda qat'iy bo'lsin - ishonchsiz manbadan olinmasin.
Keyingi bo'limda ikki tomon umumiy kalitga qanday kelishishini - kalit almashinuvini ko'ramiz.
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.