Parollarni xeshlash (bcrypt)
Foydalanuvchi ro‘yxatdan o‘tayotganda parol kiritadi. Server bu parolni qandaydir tarzda saqlashi kerak, aks holda keyingi safar uni tekshirib bo‘lmaydi. Savol shundaki, qanday saqlash kerak?
Eng muhim qoida: parol hech qachon ochiq ko‘rinishda saqlanmaydi. Server faqat parolning qaytarib bo‘lmaydigan "izi"ni, ya’ni xeshini saqlaydi. Bu darsda to‘g‘ri xeshlash usulini va Go’da bcrypt bilan ishlashni o‘rganamiz.
Nima uchun bu muhim?
Ma’lumotlar bazasi o‘g‘irlanishi mumkin. Buning sababi SQL injection, eskirgan server, zaxira nusxaning noto‘g‘ri joyda qolishi yoki ichki xodim bo‘lishi mumkin. Bunday voqealar yirik kompaniyalarda ham bo‘lib turadi.
Baza o‘g‘irlanganda parollar qanday saqlanganiga qarab oqibat keskin farq qiladi:
| Saqlash usuli | Baza o‘g‘irlansa |
|---|---|
| Ochiq matn | Barcha parollar darhol ochiladi |
| Shifrlangan | Kalit ham topilsa, barcha parollar ochiladi |
| MD5 yoki SHA-256 | Oddiy parollar bir necha soat, hatto daqiqalarda topiladi |
bcrypt, argon2id |
Har bir parolni topish juda qimmat va sekin |
Ko‘p odamlar bir xil parolni bir nechta saytda ishlatadi. Shu sababli sizning bazangizdan sizib chiqqan parol foydalanuvchining email yoki bank akkauntini ochishga ham xizmat qilishi mumkin.
Nima uchun oddiy xesh yetarli emas?
Xesh funksiya istalgan matnni qat’iy uzunlikdagi "barmoq izi"ga aylantiradi. Bu jarayonni teskari yo‘nalishda bajarib bo‘lmaydi. Xeshlash haqida umumiy ma’lumot Hashing darsida berilgan.
SHA-256 ham qaytarib bo‘lmaydigan xesh. Unda muammo nima?
1. U juda tez. SHA-256 ma’lumotni tez tekshirish uchun yaratilgan. Zamonaviy videokarta sekundiga milliardlab SHA-256 xeshini hisoblay oladi. Hujumchi mashhur parollar lug‘atidagi barcha so‘zlarni bir necha soniyada sinab chiqadi.
2. Bir xil parol — bir xil xesh. 123456 parolini ishlatgan barcha foydalanuvchilarning xeshi bir xil bo‘ladi. Hujumchi tayyor jadvallardan (rainbow tables) foydalanib, ularni bir zumda topadi.
Bu qatorni internet qidiruv tizimiga kiritsangiz, parol darhol topiladi.
bcrypt qanday hal qiladi?
bcrypt — parollar uchun maxsus ishlab chiqilgan xeshlash algoritmi. U yuqoridagi ikkala muammoni hal qiladi:
- Tuz (salt). Har bir parol uchun tasodifiy qiymat yaratiladi va parolga qo‘shiladi. Bir xil parol har safar boshqacha xesh beradi. Tayyor jadvallar ishlamaydi.
- Sozlanadigan sekinlik (cost). Algoritm ataylab sekin ishlaydi. Server uchun bitta parolni tekshirishga ketadigan 50–100 ms sezilmaydi. Milliardlab variantni sinab ko‘rmoqchi bo‘lgan hujumchi uchun esa bu yuzlab yillar degani.
bcrypt natijasi shunday ko‘rinadi:
$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
└┬┘└┬┘└─────────┬────────────┘└──────────────┬──────────────┘
│ │ salt (22 belgi) xesh (31 belgi)
│ cost = 10
algoritm versiyasi
Tuz va cost xeshning o‘zida saqlanadi. Shuning uchun ularni alohida ustunda saqlash shart emas. Bazada bitta TEXT ustun yetarli.
O‘rnatish
bcrypt Go jamoasi qo‘llab-quvvatlaydigan golang.org/x/crypto modulida joylashgan:
Xeshlash va tekshirish
Xesh: $2a$10$zVQQA.RX8jdSZVsp91BX/.xubGQ4gVpxfxXUAsI.0JwWVjAS5Ph4m
Xesh: $2a$10$IVsXJdYPe/U/E.ZjxooZ3e9lFXsO/NQiDn41PHpDhVa6VwtDkqwRO
To‘g‘ri parol: true
Noto‘g‘ri parol: true
Xesh qiymatlari har safar boshqacha chiqadi, sizda ham boshqacha bo‘ladi.
Kod qanday ishladi:
GenerateFromPasswordtasodifiy tuz yaratadi, parolni xeshlaydi va natijani yuqorida ko‘rilgan formatda qaytaradi.CompareHashAndPasswordxeshdan tuz va cost qiymatini oladi, kiritilgan parolni xuddi shu sozlamalar bilan xeshlaydi va natijalarni solishtiradi. Mos kelsanil, aks holdaErrMismatchedHashAndPasswordqaytaradi.- Ikkala xesh har xil bo‘lsa ham, ikkalasi ham to‘g‘ri parolni tasdiqlaydi.
Warning
Xeshlarni == yoki bytes.Equal bilan solishtirmang va parolni qayta xeshlab natijani solishtirishga urinmang. Tuz har safar yangi bo‘lgani uchun bu ishlamaydi. Tekshirish uchun faqat CompareHashAndPassword ishlatiladi.
Cost qiymatini tanlash
cost — sekinlik darajasi. Qiymat 1 ga oshsa, hisoblash vaqti ikki barobar ortadi.
| Konstanta | Qiymat |
|---|---|
bcrypt.MinCost |
4 |
bcrypt.DefaultCost |
10 |
bcrypt.MaxCost |
31 |
Cost qiymatining ta’sirini o‘z kompyuteringizda o‘lchab ko‘ring:
| main.go | |
|---|---|
Natija protsessorga bog‘liq. Qoida sifatida: server uskunangizda bitta xeshlash taxminan 100–250 ms oladigan eng katta cost qiymatini tanlang. Kamida 10 bo‘lishi kerak. Qiymatni juda katta qilmang: har bir login so‘rovi CPU’ni shuncha vaqt band qiladi. Bir vaqtda ko‘p login bo‘lsa, server sekinlashadi.
72 bayt cheklovi
bcrypt parolning faqat birinchi 72 baytini ishlatadi. golang.org/x/crypto/bcrypt paketi bundan uzun parolni jimgina qisqartirmaydi, balki bcrypt.ErrPasswordTooLong xatosini qaytaradi:
long := strings.Repeat("a", 73)
_, err := bcrypt.GenerateFromPassword([]byte(long), bcrypt.DefaultCost)
fmt.Println(err) // bcrypt: password length exceeds 72 bytes
Shuning uchun parol uzunligini validatsiya bosqichida cheklang: validate:"min=8,max=72". Validator belgilarni, bcrypt esa baytlarni sanaydi. Kirill harflari va ‘ kabi belgilar bir necha baytdan iborat. Aniqroq bo‘lish uchun baytlarni ham tekshiring:
Go’da len() satrdagi baytlar sonini qaytaradi.
Real loyihada: password paketi
bcryptni butun kod bo‘ylab to‘g‘ridan-to‘g‘ri chaqirish o‘rniga, uni kichik paketga o‘rab qo‘yish qulay. Shunda cost qiymati va qoidalar bitta joyda turadi:
Parol to‘g‘ri: true | Qayta xeshlash kerak: true
Yangi cost: 12
parol kamida 8 belgidan iborat bo‘lishi kerak
Cost’ni bosqichma-bosqich oshirish
Kompyuterlar yildan yilga tezlashadi, shuning uchun vaqt o‘tishi bilan cost qiymatini oshirish kerak bo‘ladi. Lekin eski xeshlarni qayta hisoblab bo‘lmaydi, chunki asl parollar ma’lum emas.
Yechim Verify funksiyasidagi needsRehash qiymatida. Foydalanuvchi tizimga kirganda parol to‘g‘ri bo‘lsa va xesh eski cost bilan yaratilgan bo‘lsa, parol yangi cost bilan qayta xeshlanadi va bazada yangilanadi. Faqat shu paytda asl parol serverda bo‘ladi. Shu tariqa foydalanuvchilar sezmagan holda barcha faol akkauntlar yangi sozlamaga o‘tadi.
Bazada saqlash
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Ustun nomi password emas, password_hash. Bu kichik tafsilot, lekin kodni o‘qigan har bir dasturchiga ustunda ochiq parol emasligini aniq bildiradi.
Go modelida xesh hech qachon JSON’ga tushmasligi kerak:
type User struct {
ID int64 `json:"id"`
Email string `json:"email"`
PasswordHash string `json:"-"`
}
Boshqa algoritmlar
bcrypt 1999-yildan beri ishlatiladi va hanuzgacha xavfsiz hisoblanadi. Yangi loyihalar uchun OWASP tashkiloti Argon2id algoritmini birinchi o‘rinda tavsiya qiladi. U hisoblash vaqtidan tashqari xotira sarfini ham sozlash imkonini beradi, bu esa videokartadagi hujumlarni qiyinlashtiradi. Go’da u golang.org/x/crypto/argon2 paketida mavjud.
Qaysi algoritmni tanlasangiz ham, asosiy qoidalar bir xil:
- faqat parollar uchun mo‘ljallangan algoritmdan foydalaning:
argon2id,bcryptyokiscrypt; - MD5, SHA-1 va SHA-256’ni parollar uchun ishlatmang;
- o‘zingiz xeshlash algoritmi o‘ylab topmang.
Tavsiyalar va parametrlar vaqt o‘tishi bilan yangilanib turadi. Loyihani boshlashdan oldin OWASP Password Storage Cheat Sheet hujjatining joriy versiyasini tekshiring.
Xulosa
- Parol ochiq ko‘rinishda ham, oddiy xesh ko‘rinishida ham saqlanmaydi.
bcrypthar bir parolga tasodifiy tuz qo‘shadi va ataylab sekin ishlaydi.- Xeshlash
GenerateFromPasswordbilan, tekshirish faqatCompareHashAndPasswordbilan bajariladi. - Cost qiymati server uskunasiga qarab tanlanadi: bitta xesh taxminan 100–250 ms. Kamida
10. bcrypt72 baytdan uzun parolni rad etadi. Uzunlikni oldindan tekshiring.- Cost’ni oshirganda eski xeshlar foydalanuvchi tizimga kirgan paytda yangilanadi.
Keyingi darsda ma’lumotlar bazasi paroli kabi maxfiy sozlamalarni koddan ajratib, .env va muhit o‘zgaruvchilari orqali boshqarishni o‘rganamiz.