Password Hashing — BCrypt.Net, hash va verify, SALT_ROUNDS
Muammodan boshlaylik
var f = new Foydalanuvchi { Email = dto.Email, Parol = dto.Parol }; // ochiq matnda
Baza sizib chiqdi (SQL injection, buzilgan zaxira nusxa, ichki xodim). Hujumchi barcha parollarni ochiq o'qiydi. Odamlar bir xil parolni ko'p joyda ishlatadi — endi ularning email, bank va ijtimoiy tarmoq hisoblari ham xavf ostida.
"Shifrlab saqlayman" — shifrlash kaliti ham serverda, baza bilan birga o'g'irlanadi.
Hashing nima?
Hash — ma'lumotdan qaytarib bo'lmaydigan "barmoq izi" yasash:
"parol123" ──hash──▶ "$2a$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW"
- Bir xil kirish — doim bir xil tekshiruv natijasi
- Hash dan parolni qaytarib tiklab bo'lmaydi
- Kirishdagi kichik o'zgarish — butunlay boshqa hash
Login da: kiritilgan parolni hash lab, saqlangan hash bilan solishtiramiz. Parolning o'zi hech qayerda saqlanmaydi.
| Shifrlash | Hashing | |
|---|---|---|
| Qaytarish | Kalit bilan — mumkin | Mumkin emas |
| Parollar uchun | ❌ | ✅ |
Hayotiy o'xshatma
Go'shtni qiymalagich. Go'shtdan qiyma qilish — oson; qiymadan qaytadan butun go'sht bo'lagini yasash — imkonsiz. Lekin xuddi shu go'shtni xuddi shu qiymalagichdan o'tkazsangiz — qiymaning "naqshi" mos keladi. Tuz (salt) — har bir odamning go'shtiga maxsus ziravor: ikki odam bir xil go'sht olib kelsa ham, qiyma har xil chiqadi.
Nega SHA256 yetmaydi
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(parol)); // ❌ parollar uchun emas
- Juda tez — zamonaviy GPU soniyasiga milliardlab SHA256 hisoblaydi: 8 belgili parollarni bir necha soatda tanlab chiqadi
- Tuzsiz — bir xil parollar bir xil hash: oldindan hisoblangan jadvallar (rainbow tables) bilan darhol topiladi; bitta parol topilsa — uni ishlatgan barcha foydalanuvchilar
Parollar uchun — ataylab sekin, tuzli algoritmlar: bcrypt, Argon2, PBKDF2, scrypt.
BCrypt.Net
dotnet add package BCrypt.Net-Next
Hash lash — ro'yxatdan o'tishda
string xesh = BCrypt.Net.BCrypt.HashPassword(dto.Parol, workFactor: 12);
var f = new Foydalanuvchi { Email = dto.Email.ToLowerInvariant(), ParolXesh = xesh };
$2a$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
│ │ └───────── salt (22) ─────────┘└────────── hash (31) ──────────┘
│ └── work factor (cost) = 12 → 2^12 = 4096 iteratsiya
└── algoritm versiyasi
- Salt avtomatik yaratiladi va hash satrining ichida saqlanadi — alohida ustun kerak emas
- Bir xil parol ikki marta hash lansa — har xil natija (har xil salt)
Tekshirish — login da
var f = await db.Foydalanuvchilar.FirstOrDefaultAsync(x => x.Email == dto.Email.ToLowerInvariant(), ct);
if (f is null || !BCrypt.Net.BCrypt.Verify(dto.Parol, f.ParolXesh))
return Unauthorized("Email yoki parol noto'g'ri");
Verify hash satridan salt va work factor ni o'zi oladi.
Work factor — sekinlik darajasi
| Work factor | Bitta hash (taxminan) |
|---|---|
| 10 | ~60 ms |
| 12 | ~250 ms |
| 14 | ~1 s |
Har +1 — ikki baravar sekinroq. Foydalanuvchi uchun 250 ms login — sezilmaydi; hujumchi uchun esa milliard urinish — yuzlab yil.
[!TIP] Work factor ni serveringizda bitta hash ~250 ms atrofida bo'ladigan qilib tanlang (odatda 11–12). Vaqt o'tib serverlar tezlashadi — qiymatni oshiring; eski hash lar foydalanuvchi keyingi safar kirganda yangi factor bilan qayta hash lanadi:
if (BCrypt.Net.BCrypt.Verify(parol, f.ParolXesh) &&
BCrypt.Net.BCrypt.PasswordNeedsRehash(f.ParolXesh, newMinimumWorkLoad: 12))
{
f.ParolXesh = BCrypt.Net.BCrypt.HashPassword(parol, 12);
await db.SaveChangesAsync(ct);
}
ASP.NET Core Identity ning PasswordHasher i
Tashqi paketsiz — o'rnatilgan PBKDF2 (HMAC-SHA512, 100 000+ iteratsiya):
using Microsoft.AspNetCore.Identity;
var hasher = new PasswordHasher<Foydalanuvchi>();
f.ParolXesh = hasher.HashPassword(f, dto.Parol);
var natija = hasher.VerifyHashedPassword(f, f.ParolXesh, dto.Parol);
if (natija == PasswordVerificationResult.Failed) return Unauthorized();
if (natija == PasswordVerificationResult.SuccessRehashNeeded) { /* qayta hash lash */ }
To'liq foydalanuvchi boshqaruvi (ro'yxatdan o'tish, email tasdiqlash, parolni tiklash, lockout, 2FA) kerak bo'lsa — ASP.NET Core Identity framework ining o'zi.
Xavfsizlik qoidalari
- Parol — hech qachon ochiq yoki qaytariladigan shifrda saqlanmaydi
- Faqat bcrypt / Argon2 / PBKDF2 — SHA256/MD5 emas
- Login xatosi — bitta umumiy xabar: "Email yoki parol noto'g'ri" (qaysi biri noto'g'ri ekanini aytmang)
- Login ga rate limit (70-dars) va bir necha muvaffaqiyatsiz urinishdan keyin vaqtincha bloklash
- Parol log larga, xato xabarlariga, API javoblariga tushmasin
- Minimal uzunlik (kamida 8), mashhur parollar ro'yxatiga qarshi tekshiruv
- HTTPS — parol tarmoqda ochiq ketmasin
[!WARNING] Foydalanuvchi topilmaganda
Verifyni chaqirmasdan darhol javob qaytarish — javob vaqti farqi orqali qaysi email lar ro'yxatdan o'tganini aniqlash mumkin (timing attack). Yuqori xavfsizlik talab qilinsa — topilmaganda ham soxta hash bilanVerifybajaring, javob vaqti bir xil bo'lsin.
Tez-tez uchraydigan xatolar
HashPassword natijasini == bilan solishtirish:
if (BCrypt.Net.BCrypt.HashPassword(parol) == f.ParolXesh) // ❌ doim false — salt har xil
if (BCrypt.Net.BCrypt.Verify(parol, f.ParolXesh)) // ✅
Hash ustuni qisqa: bcrypt — 60 belgi; HasMaxLength(50) — kesilib qoladi va hech kim kira olmaydi.
Email ni normalizatsiya qilmaslik — Lola@Mail.uz va lola@mail.uz — ikki xil hisob.
Xulosa
Parollar — faqat hash ko'rinishida: qaytarib bo'lmaydi, login da hash lanib solishtiriladi. SHA256/MD5 — juda tez va tuzsiz, parollar uchun yaramaydi; kerak — sekin, tuzli algoritmlar: bcrypt, Argon2, PBKDF2.
BCrypt.Net: HashPassword(parol, workFactor: 12) — salt avtomatik, hash ichida; Verify(parol, xesh) — tekshirish (== emas). Work factor — ~250 ms; PasswordNeedsRehash bilan oshirish. Muqobil — Identity ning PasswordHasher<T>. Umumiy xato xabari, rate limit, HTTPS.
Keyingi dars
59-dars: JWT nima? — Header, Payload, Signature, qanday ishlaydi