Password Hashing — BCrypt.Net, hash va verify, SALT_ROUNDS

~20 daq · .NET Backend · Autentifikatsiya va Avtorizatsiya

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
  1. Juda tez — zamonaviy GPU soniyasiga milliardlab SHA256 hisoblaydi: 8 belgili parollarni bir necha soatda tanlab chiqadi
  2. 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 Verify ni 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 bilan Verify bajaring, 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