4-bo‘lim
Parollarni saqlash
Nima uchun SHA-256 parol uchun yaramaydi, tuz nima beradi va Argon2id parametrlari qanday tanlanadi.
Ushbu bo‘lim mundarijasi
Parol xeshlari o'g'irlanadigan ma'lumotlar ro'yxatida birinchi o'rinda turadi. Ular qanday saqlanganini belgilaydi: hujumchi bir necha soatda hammasini ochadimi yoki umuman ocha olmaydimi.
Noto'g'ri usullar #
function qator(string $usul, string $natija, string $baho): void
{
printf("%-16s %-34s %s%s", $usul, $natija, $baho, PHP_EOL);
}
$parol = 'Parol123';
qator('ochiq matn', $parol, 'HALOKATLI');
qator('base64', base64_encode($parol), 'HALOKATLI - kodlash');
qator('md5', md5($parol), 'JUDA YOMON');
qator('sha256', substr(hash('sha256', $parol), 0, 32) . '...', 'YOMON');
qator('sha256+tuz', substr(hash('sha256', 'tuz' . $parol), 0, 32) . '...',
'YETARLI EMAS');
ochiq matn Parol123 HALOKATLI
base64 UGFyb2wxMjM= HALOKATLI - kodlash
md5 6a5be8fb198f94424ffeed351c18ddf6 JUDA YOMON
sha256 46fac07aae8ccfcfc7e52b540af49b9c... YOMON
sha256+tuz ad338e3d820de0beccda2902e437e47f... YETARLI EMAS
sha256 ham yetarli emasUchta alohida muammo bor:
1. Juda tez. 3-bo'limda o'lchadik: oddiy noutbukda soniyasiga million atrofida. GPU da bu son yuz millionlarga chiqadi. Hujumchi lug'atdagi barcha parollarni sinab chiqishga bir necha soat sarflaydi.
2. Tuzsiz - oldindan hisoblangan jadval ishlaydi. Agar
tuz bo'lmasa, sha256('Parol123') butun dunyoda bir xil.
Hujumchi millionlab parolning xeshini oldindan hisoblab
qo'yadi (rainbow table) va faqat qidiradi.
3. Bir xil parol - bir xil xesh. Bazadagi ikki foydalanuvchining xeshi bir xil bo'lsa, ularning paroli ham bir xil ekani ko'rinib turadi.
Uchinchi muammoni ko'rsatamiz:
$xesh1 = hash('sha256', 'Parol123');
$xesh2 = hash('sha256', 'Parol123');
echo 'Husanboy: ', substr($xesh1, 0, 24), '...', PHP_EOL;
echo 'Malika: ', substr($xesh2, 0, 24), '...', PHP_EOL;
echo 'bir xilmi: ', $xesh1 === $xesh2 ? 'HA - parol ham bir xil' : 'yoq',
PHP_EOL;
Husanboy: 46fac07aae8ccfcfc7e52b54...
Malika: 46fac07aae8ccfcfc7e52b54...
bir xilmi: HA - parol ham bir xil
Tuz (salt) nima beradi #
// Har foydalanuvchiga alohida tasodifiy tuz
$tuz1 = random_bytes(16);
$tuz2 = random_bytes(16);
$xesh1 = hash('sha256', $tuz1 . 'Parol123');
$xesh2 = hash('sha256', $tuz2 . 'Parol123');
echo 'parollar bir xil, xeshlar bir xilmi: ',
$xesh1 === $xesh2 ? 'ha' : 'YOQ', PHP_EOL;
echo 'tuz uzunligi: ', strlen($tuz1), ' bayt', PHP_EOL;
echo 'tuz maxfiymi: yoq - u xesh bilan birga saqlanadi', PHP_EOL;
parollar bir xil, xeshlar bir xilmi: YOQ
tuz uzunligi: 16 bayt
tuz maxfiymi: yoq - u xesh bilan birga saqlanadi
Bu ko'pchilikni ajablantiradi: tuz maxfiy emas va xesh bilan yonma-yon saqlanadi.
Uning vazifasi boshqa:
| Tuz beradi | Tuz bermaydi |
|---|---|
| Oldindan hisoblangan jadvalni foydasiz qiladi | Hujumni imkonsiz qilmaydi |
| Har parolni alohida sindirishga majbur qiladi | Bitta parolni himoyalamaydi |
| Bir xil parollarni yashiradi | Tezlikni sekinlashtirmaydi |
Ya'ni tuz hujumni ko'paytiradi, lekin sekinlashtirmaydi. Million foydalanuvchi bo'lsa, hujumchi million marta ish qilishi kerak - ammo har biri baribir tez.
Shuning uchun tuz yetarli emas. Sekinlik ham kerak.
To'g'ri yechim: password_hash #
$parol = 'Parol123';
$xesh = password_hash($parol, PASSWORD_DEFAULT);
echo 'uzunlik: ', strlen($xesh), ' belgi', PHP_EOL;
echo 'boshlanish: ', substr($xesh, 0, 7), PHP_EOL;
echo 'formati: $algoritm$narx$tuz+xesh', PHP_EOL;
print_r(password_get_info($xesh));
// Har chaqiruvda BOSHQA xesh - chunki tuz tasodifiy
$ikkinchi = password_hash($parol, PASSWORD_DEFAULT);
echo 'ikki xesh bir xilmi: ', $xesh === $ikkinchi ? 'ha' : 'YOQ', PHP_EOL;
// Lekin ikkalasi ham tekshiruvdan o'tadi
var_dump(password_verify($parol, $xesh));
var_dump(password_verify($parol, $ikkinchi));
var_dump(password_verify('NotogriParol', $xesh));
uzunlik: 60 belgi
boshlanish: $2y$10$
formati: $algoritm$narx$tuz+xesh
Array
(
[algo] => 2y
[algoName] => bcrypt
[options] => Array
(
[cost] => 10
)
)
ikki xesh bir xilmi: YOQ
bool(true)
bool(true)
bool(false)
password_hash sizning o'rningizga hamma ishni qiladi| Vazifa | Kim bajaradi |
|---|---|
| Tasodifiy tuz yaratish | password_hash |
| Tuzni xesh bilan saqlash | password_hash (formatning bir qismi) |
| Algoritm va parametrlarni yozib qo'yish | password_hash |
| Tekshirishda tuzni ajratib olish | password_verify |
| Doimiy vaqtda solishtirish | password_verify |
Sizga faqat natijani satr ustuniga saqlash qoladi.
Ustun kengligi: VARCHAR(255). Hozirgi bcrypt 60 belgi,
Argon2id ~97 belgi, lekin kelajakdagi algoritmlar uzunroq
bo'lishi mumkin.
Argon2id va bcrypt #
$parol = 'Parol123';
$variantlar = [
'bcrypt' => PASSWORD_BCRYPT,
'argon2i' => PASSWORD_ARGON2I,
'argon2id' => PASSWORD_ARGON2ID,
];
foreach ($variantlar as $nom => $algo) {
$x = password_hash($parol, $algo);
printf("%-9s %-3d belgi %s%s", $nom, strlen($x),
explode('$', $x)[1], PHP_EOL);
}
bcrypt 60 belgi 2y
argon2i 96 belgi argon2i
argon2id 97 belgi argon2id
Narx parametri #
$parol = 'Parol123';
// Nazariy nisbat: har +1 cost ishni ikki barobar oshiradi
printf("%-8s %-12s %s%s", 'cost', 'nisbiy ish', 'ustun', PHP_EOL);
echo str_repeat('-', 40), PHP_EOL;
foreach ([4, 8, 10, 12, 14] as $narx) {
$nisbat = 2 ** ($narx - 4);
printf("%-8d %-12s %s%s", $narx, 'x' . $nisbat,
str_repeat('#', min(40, $nisbat)), PHP_EOL);
}
// Amalda ham shunday: eng past va eng yuqori narxni o'lchaymiz
$olcha = static function (int $narx) use ($parol): float {
$b = hrtime(true);
password_hash($parol, PASSWORD_BCRYPT, ['cost' => $narx]);
return (hrtime(true) - $b) / 1e6;
};
$past = $olcha(4);
$yuqori = $olcha(12);
echo PHP_EOL;
printf('cost=12 cost=4 dan sekinroqmi: %s%s',
$yuqori > $past ? 'ha' : 'yoq', PHP_EOL);
printf('nisbat 100 martadan kopmi: %s%s',
$yuqori / $past > 100 ? 'ha' : 'yoq', PHP_EOL);
cost nisbiy ish ustun
----------------------------------------
4 x1 #
8 x16 ################
10 x64 ########################################
12 x256 ########################################
14 x1024 ########################################
cost=12 cost=4 dan sekinroqmi: ha
nisbat 100 martadan kopmi: ha
Aniq millisekundlar mashinaga bog'liq, lekin nisbat
har joyda bir xil: cost bir birlikka oshsa, ish ikki
barobar ko'payadi. cost=4 dan cost=12 gacha - 256 marta.
Qoida: eng sekin qiymatni tanlang, foydalanuvchi sezmasa bas.
| Vaziyat | Tavsiya |
|---|---|
| Veb ilova, oddiy server | bcrypt cost 12, ~100-250 ms |
| Kuchli server | cost 13-14 |
| Mobil/IoT, zaif protsessor | cost 10-11 |
| Argon2id | memory_cost 64 MB, time_cost 4 |
Ikki muhim eslatma:
1. Narxni vaqt o'tishi bilan oshirib boring. Uskuna
tezlashadi, 2015-yildagi cost=10 bugun kam.
2. DoS xavfini unutmang. cost=15 va soniyasiga 100 ta
kirish urinishi serveringizni yiqitadi. Kirish sahifasiga
tezlik chegarasi ham kerak.
Argon2id da memory_cost ayniqsa muhim: u GPU hujumini
qimmatlashtiradi, chunki har parallel oqim uchun alohida
xotira kerak bo'ladi.
Algoritmni yangilash #
// Eski xesh - past narx bilan yaratilgan
$eskiXesh = password_hash('Parol123', PASSWORD_BCRYPT, ['cost' => 4]);
$yangiSozlama = ['cost' => 12];
echo 'eski xesh narxi: ', password_get_info($eskiXesh)['options']['cost'],
PHP_EOL;
echo 'yangilash kerakmi: ',
password_needs_rehash($eskiXesh, PASSWORD_BCRYPT, $yangiSozlama)
? 'HA' : 'yoq', PHP_EOL;
// Kirish jarayonida - parol ochiq matnda MAVJUD bo'lgan yagona payt
function kirish(string $parol, string $saqlangan): string
{
if (!password_verify($parol, $saqlangan)) {
return 'RAD: parol notogri';
}
if (password_needs_rehash($saqlangan, PASSWORD_BCRYPT, ['cost' => 12])) {
$yangi = password_hash($parol, PASSWORD_BCRYPT, ['cost' => 12]);
return 'OK + xesh yangilandi (narx: '
. password_get_info($yangi)['options']['cost'] . ')';
}
return 'OK';
}
echo kirish('Parol123', $eskiXesh), PHP_EOL;
echo kirish('Notogri', $eskiXesh), PHP_EOL;
eski xesh narxi: 4
yangilash kerakmi: HA
OK + xesh yangilandi (narx: 12)
RAD: parol notogri
Bazadagi barcha xeshlarni bir vaqtda yangilab bo'lmaydi - ochiq parol sizda yo'q.
Yagona payt - foydalanuvchi kirganda: shu daqiqada parol qo'lingizda bo'ladi.
Shuning uchun migratsiya asta-sekin boradi:
| Vaqt | Yangilangan foydalanuvchilar |
|---|---|
| 1-hafta | Faol foydalanuvchilar |
| 1-oy | Ko'pchilik |
| 1-yil | Deyarli hammasi |
| Hech qachon | Tashlab ketilgan hisoblar |
Oxirgi qatorga e'tibor bering: yillar davomida kirmagan hisoblarning xeshi eski qoladi. Ularni majburiy parol almashtirishga yo'naltirish yoki hisobni muzlatish mumkin.
bcrypt ning ikki tuzog'i #
// 1. bcrypt faqat birinchi 72 BAYTNI hisobga oladi
$uzun = str_repeat('a', 72);
$yanaUzun = $uzun . 'BUTUNLAY-BOSHQA-QISM';
$xesh = password_hash($uzun, PASSWORD_BCRYPT);
echo 'birinchi parol uzunligi: ', strlen($uzun), PHP_EOL;
echo 'ikkinchi parol uzunligi: ', strlen($yanaUzun), PHP_EOL;
echo 'ikkinchisi ham otadimi: ',
password_verify($yanaUzun, $xesh) ? 'HA - XAVF' : 'yoq', PHP_EOL;
// 2. Argon2id da bunday chegara yo'q
$xesh2 = password_hash($uzun, PASSWORD_ARGON2ID);
echo 'argon2id da otadimi: ',
password_verify($yanaUzun, $xesh2) ? 'ha' : 'yoq', PHP_EOL;
birinchi parol uzunligi: 72
ikkinchi parol uzunligi: 92
ikkinchisi ham otadimi: HA - XAVF
argon2id da otadimi: yoq
bcrypt uzun parolni kesib tashlaydi. Bu odatda muammo emas (72 bayt uzun parol), lekin ikkita holatda xavfli:
| Holat | Nima uchun xavfli |
|---|---|
| Parolni oldin xeshlab, keyin bcrypt ga berish | Hex xesh 64 belgi, base64 - 44; chegaraga yaqin |
| Parol menejeri yaratgan uzun iboralar | 72 baytdan keyingisi e'tiborsiz |
| Kirill yoki emoji parol | Belgi ikki-to'rt bayt - chegara tezroq keladi |
Uchinchisi ayniqsa muhim: "Мойпарольочень..." - 36 kirill belgisi allaqachon 72 bayt.
Yechim:
| Yechim | Izoh |
|---|---|
| Argon2id ishlatish | Chegara yo'q |
| Parolni avval HMAC bilan qisqartirish | hash_hmac('sha256', $parol, $qalampir, true) - 32 bayt |
| Parol uzunligini 72 bayt bilan cheklash | Foydalanuvchiga noqulay |
Ikkinchi yechim keyingi bo'limda ko'radigan qalampir bilan birga qulay chiqadi.
Qalampir (pepper) #
// Qalampir - BAZADA EMAS, ilova sozlamasida saqlanadigan maxfiy kalit
$qalampir = hex2bin(str_repeat('7f', 32)); // real kodda: muhit o'zgaruvchisi
function qalampirliXesh(string $parol, string $qalampir): string
{
// HMAC parolni 32 baytga keltiradi va qalampirni qo'shadi
$tayyor = hash_hmac('sha256', $parol, $qalampir, true);
return password_hash(base64_encode($tayyor), PASSWORD_ARGON2ID);
}
function qalampirliTekshir(string $parol, string $xesh, string $qalampir): bool
{
$tayyor = hash_hmac('sha256', $parol, $qalampir, true);
return password_verify(base64_encode($tayyor), $xesh);
}
$xesh = qalampirliXesh('Parol123', $qalampir);
var_dump(qalampirliTekshir('Parol123', $xesh, $qalampir));
var_dump(qalampirliTekshir('Notogri', $xesh, $qalampir));
// Boshqa qalampir bilan - o'tmaydi
$boshqa = hex2bin(str_repeat('80', 32));
var_dump(qalampirliTekshir('Parol123', $xesh, $boshqa));
bool(true)
bool(false)
bool(false)
| Beradi | Bermaydi |
|---|---|
| Faqat baza o'g'irlansa - xeshlar foydasiz | Server butunlay buzilsa - yordam bermaydi |
| Bir vaqtda ikki tizimni buzishni talab qiladi | Zaif parolni kuchaytirmaydi |
base64_encode orqali 72 bayt muammosini yechadi | Sekinlik bermaydi |
Qalampir qo'shimcha qatlam, asosiy himoya emas. Uni
qo'shishdan oldin password_hash to'g'ri ishlatilganiga
ishonch hosil qiling.
Eng katta amaliy muammo - qalampirni almashtirish. U o'zgarsa, barcha xeshlar yaroqsiz bo'ladi. Shuning uchun uni versiyalash kerak:
xesh_versiyasi | qalampir_id | xesh
Kalitlarni almashtirish 18-bo'limning mavzusi.
Qo'shimcha himoya qatlamlari #
| Qatlam | Nima beradi |
|---|---|
| Tezlik chegarasi (IP va hisob bo'yicha) | Onlayn taxmin qilishni to'xtatadi |
| Ikki bosqichli tasdiqlash | Parol o'g'irlansa ham himoya qoladi |
| Ma'lum sizib chiqqan parollarni rad etish | Eng ko'p ishlatiladigan parollarni bloklaydi |
| Uzun parolga ruxsat berish | Uzunlik murakkablikdan muhimroq |
| Parolni jurnalga yozmaslik | Eng ko'p uchraydigan sizib chiqish sababi |
// Parol siyosati: uzunlikka tayaning, murakkablikka emas
function parolYaroqlimi(string $parol): string
{
$bayt = strlen($parol);
return match (true) {
$bayt < 12 => 'RAD: kamida 12 belgi kerak',
$bayt > 4096 => 'RAD: juda uzun (DoS himoyasi)',
in_array(strtolower($parol), ['parol123456', '123456789012'], true)
=> 'RAD: juda keng tarqalgan parol',
default => 'QABUL',
};
}
foreach (['qisqa', 'Parol123', 'parol123456',
'togri-ot-batareya-mixi', str_repeat('a', 5000)] as $p) {
printf('%-26s %s%s',
strlen($p) > 26 ? substr($p, 0, 23) . '...' : $p,
parolYaroqlimi($p), PHP_EOL);
}
qisqa RAD: kamida 12 belgi kerak
Parol123 RAD: kamida 12 belgi kerak
parol123456 RAD: kamida 12 belgi kerak
togri-ot-batareya-mixi QABUL
aaaaaaaaaaaaaaaaaaaaaaa... RAD: juda uzun (DoS himoyasi)
- Bir xil parolni ikki marta
sha256bilan xeshlang - natijalar bir xilmi? - Har biriga alohida tuz qo'shing va natijani solishtiring.
- Nima uchun tuz maxfiy bo'lishi shart emasligini tushuntiring.
password_hashni ikki marta chaqiring - xeshlar farq qildimi?- Ikkala xeshni ham
password_verifybilan tekshiring. costni 4 dan 12 gacha oshirib, vaqt qanday o'zgarishini kuzating.password_needs_rehashbilan yangilash oqimini yozing.- 72 baytdan uzun parol bilan bcrypt tuzog'ini takrorlang.
- Xuddi shu sinovni Argon2id da bajaring.
- Qalampirli xeshlash funksiyasini yozing va noto'g'ri qalampir bilan sinang.
Xulosa #
- Parolni ochiq matnda, base64 da yoki MD5 bilan saqlash - halokatli.
sha256ham yaramaydi: u juda tez va tuzsiz.- Tuz oldindan hisoblangan jadvallarni foydasiz qiladi va bir xil parollarni yashiradi.
- Lekin tuz hujumni sekinlashtirmaydi - u faqat ko'paytiradi.
password_hashtuz yaratish, saqlash va tekshirishni o'zi bajaradi.- Har chaqiruvda boshqa xesh chiqadi, lekin
password_verifyikkalasini ham tanidi. - Narx (
cost) har birlik oshganda vaqtni ikki barobar oshiradi. - Yangilash faqat kirish paytida mumkin -
password_needs_rehash. - bcrypt faqat birinchi 72 baytni hisobga oladi; Argon2id da bunday chegara yo'q.
- Qalampir - qo'shimcha qatlam: baza o'g'irlansa xeshlarni foydasiz qiladi.
Keyingi bo'limda xabar butunligi va HMAC 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.