Autentifikatsiya va avtorizatsiya
Deyarli har bir ilova foydalanuvchini taniydi (autentifikatsiya) va unga nima qilishga ruxsat berilganini hal qiladi (avtorizatsiya). Ushbu darsda biz bu ikki tushunchani ajratamiz, parollarni xavfsiz saqlashni (hashlash va salt), hamda foydalanuvchi holatini saqlashning ikki asosiy usulini โ sessiya va tokenlarni โ ko'rib chiqamiz.
Autentifikatsiya va avtorizatsiya farqi
Bu ikki so'z ko'pincha aralashtiriladi, lekin ular butunlay boshqa narsani anglatadi:
- Autentifikatsiya (Authentication) โ "Sen kimsan?" degan savolga javob. Foydalanuvchining shaxsini tasdiqlash (login va parol, tokendan orqali);
- Avtorizatsiya (Authorization) โ "Senga nima qilishga ruxsat bor?" degan savolga javob. Tasdiqlangan foydalanuvchining huquqlarini tekshirish.
Oddiy misol: mehmonxonaga kirganingizda pasportingizni ko'rsatasiz โ bu autentifikatsiya. Sizga berilgan kalit faqat o'z xonangizni ochadi, boshqa xonalarni emas โ bu avtorizatsiya.
// Autentifikatsiya: foydalanuvchi kimligini aniqlaymiz
function authMiddleware(req, res, next) {
const foydalanuvchi = tokendanFoydalanuvchiOl(req);
if (!foydalanuvchi) {
return res.status(401).json({ xato: 'Tizimga kiring' }); // 401 = kim ekaning noma'lum
}
req.foydalanuvchi = foydalanuvchi;
next();
}
// Avtorizatsiya: bu foydalanuvchiga ruxsat bormi?
function faqatAdmin(req, res, next) {
if (req.foydalanuvchi.rol !== 'admin') {
return res.status(403).json({ xato: 'Ruxsat yo'q' }); // 403 = kimligi ma'lum, lekin ruxsat yo'q
}
next();
}
Parolni HECH QACHON ochiq saqlamang
Bu โ autentifikatsiyadagi eng muhim qoida. Parolni ma'lumotlar bazasida o'qish mumkin bo'lgan holatda saqlash โ jiddiy xato. Agar bazangiz o'g'irlansa (bu tez-tez sodir bo'ladi), barcha foydalanuvchilar parollari ochiladi. Bundan ham yomoni: odamlar ko'pincha bir parolni bir necha saytda ishlatadi.
Hashlash, salt va bcrypt
Hash funksiyasi โ bu istalgan matndan qat'iy uzunlikdagi "barmoq izi" yaratadigan bir tomonlama funksiya. Asosiy g'oya: parolni saqlamaymiz, uning hashini saqlaymiz. Foydalanuvchi kirganda, kiritgan parolining hashini hisoblab, saqlangan hash bilan solishtiramiz.
Lekin oddiy hash (masalan, SHA-256) parollar uchun yetarli emas, chunki:
- Bir xil parol doim bir xil hash beradi โ hujumchi oldindan tayyorlangan jadvallardan foydalanishi mumkin;
- Oddiy hash funksiyalari juda tez ishlaydi โ bu parolni topishga urinishlarni osonlashtiradi.
Yechim ikki qismdan iborat:
- Salt (tuz): har bir parolga tasodifiy, noyob qiymat qo'shiladi. Shu sabab bir xil ikki parol ham turli hashga ega bo'ladi;
- Sekin hash algoritmi:
bcrypt,scryptyokiargon2ataylab sekin ishlaydi, bu topishga urinishlarni juda qimmatga tushiradi.
bcrypt โ eng ko'p ishlatiladigan yechimlardan biri. U saltni avtomatik yaratadi va uni hashning ichiga joylashtiradi:
const bcrypt = require('bcrypt');
// Ro'yxatdan o'tishda: parolni hashlaymiz
async function royxatdanOtish(email, parol) {
const saltDarajasi = 12; // 'cost factor' โ qancha katta bo'lsa, shuncha sekin (xavfsiz)
const hash = await bcrypt.hash(parol, saltDarajasi);
// Bazaga FAQAT hashni saqlaymiz, asl parolni EMAS
await db.foydalanuvchiYarat({ email, parolHash: hash });
}
// Tizimga kirishda: kiritilgan parolni saqlangan hash bilan solishtiramiz
async function tizimgaKirish(email, parol) {
const foydalanuvchi = await db.foydalanuvchiTop({ email });
// Foydalanuvchi topilmasa ham, mavhum javob beramiz (quyida tushuntiriladi)
if (!foydalanuvchi) {
return { muvaffaqiyat: false, xato: 'Email yoki parol xato' };
}
// bcrypt saltni hashdan o'zi ajratib oladi va solishtiradi
const togri = await bcrypt.compare(parol, foydalanuvchi.parolHash);
if (!togri) {
return { muvaffaqiyat: false, xato: 'Email yoki parol xato' };
}
return { muvaffaqiyat: true, foydalanuvchi };
}
bcrypt.compare ni ishlatishga majburmiz โ hashlarni === bilan solishtirish ishlamaydi, chunki har safar yangi salt tufayli hash boshqacha bo'ladi. compare saltni hashning ichidan o'qib, to'g'ri solishtiradi.Foydalanuvchi holatini saqlash: HTTP holatsiz
HTTP protokoli holatsiz (stateless): har bir so'rov mustaqil, server oldingi so'rovni "eslamaydi". Unda foydalanuvchi bir marta login qilgach, keyingi so'rovlarda uni qanday taniymiz? Ikki asosiy yondashuv bor: sessiya (cookie) va token.
1-yondashuv: Sessiya (cookie asosida)
Sessiya yondashuvida holat serverda saqlanadi:
- Foydalanuvchi login qiladi, server uni tekshiradi;
- Server tasodifiy, taxmin qilib bo'lmaydigan sessiya ID yaratadi va uni o'z xotirasida (yoki Redis, bazada) foydalanuvchi bilan bog'lab saqlaydi;
- Sessiya ID cookie sifatida brauzerga yuboriladi;
- Brauzer keyingi har bir so'rovda bu cookie'ni avtomatik jo'natadi;
- Server cookie'dagi ID orqali foydalanuvchini topadi.
const session = require('express-session');
app.use(session({
secret: process.env.SESSION_SECRET, // sirni .env dan olamiz
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // JavaScript cookie'ni o'qiy olmaydi (XSS'dan himoya)
secure: true, // faqat HTTPS orqali yuboriladi
sameSite: 'lax', // CSRF'dan himoya (keyingi darsda)
maxAge: 1000 * 60 * 60 // 1 soat
}
}));
Afzalligi: server sessiyani istagan vaqtda bekor qila oladi (masalan, "barcha qurilmalardan chiqish"). Kamchiligi: server har bir sessiyani xotirada saqlashi kerak, bu ko'p serverli tizimlarda qo'shimcha muvofiqlashtirishni talab qiladi.
2-yondashuv: Token (masalan JWT)
Token yondashuvida holat mijozda saqlanadi. Server imzolangan token beradi, mijoz uni har so'rovda qaytaradi, server esa imzoni tekshirib, tokenga ishonadi (bazaga qaramasdan). JWT โ keyingi darsning to'liq mavzusi.
- Afzalligi: server holatni saqlamaydi (stateless), bu masshtablashni osonlashtiradi;
- Kamchiligi: tokenni muddatidan oldin bekor qilish qiyinroq (server holatni saqlamaydi).
Xavfsiz cookie: HttpOnly, Secure, SameSite
Agar cookie ishlatsangiz (sessiya yoki tokenni cookie'da saqlash), uni to'g'ri sozlash muhim. Uch asosiy atribut:
HttpOnly
HttpOnly belgilangan cookie'ni JavaScript o'qiy olmaydi (document.cookie orqali ko'rinmaydi). Bu XSS hujumida cookie o'g'irlanishining oldini oladi: hattoki sahifaga zararli skript kirsa ham, u sessiya cookie'ni o'qiy olmaydi.
Secure
Secure belgilangan cookie faqat HTTPS orqali yuboriladi. Bu cookie'ning shifrlanmagan tarmoqda ochiq holda o'tishining oldini oladi.
SameSite
SameSite cookie'ning boshqa saytlardan kelgan so'rovlarda yuborilishini boshqaradi. Bu CSRF hujumidan himoyaning asosiy qismidir (keyingi darsda batafsil):
SameSite=Strictโ cookie faqat o'z saytimizdan kelgan so'rovlarda yuboriladi (eng qattiq);SameSite=Laxโ asosiy navigatsiyalarda yuboriladi, lekin ko'pchilik o'zaro-sayt so'rovlarida yuborilmaydi (yaxshi standart);SameSite=Noneโ barcha holatlarda yuboriladi (faqatSecurebilan birga; ehtiyotkorlik talab qilinadi).
// Cookie'ni to'g'ri sozlash misoli (Express)
res.cookie('sessionId', sessionId, {
httpOnly: true, // JS o'qiy olmaydi
secure: true, // faqat HTTPS
sameSite: 'lax', // CSRF himoyasi
maxAge: 3600000, // 1 soat (millisekundda)
path: '/'
});
HttpOnly XSS ta'sirini kamaytiradi, lekin XSS'ning o'zini oldini olmaydi โ kirishni tozalash (keyingi dars) baribir zarur.Xulosa
- Autentifikatsiya โ "kimsan?", avtorizatsiya โ "nimaga ruxsating bor?";
- Parolni hech qachon ochiq yoki oddiy shifrlangan holda saqlamang โ
bcryptkabi sekin, salt qo'shadigan algoritm bilan hashlang; - Login xatosida har doim bir xil, mavhum xabar bering (foydalanuvchi mavjudligini fosh qilmaslik uchun);
- Holatni saqlashning ikki yo'li: server tomonidagi sessiya va mijoz tomonidagi token;
- Cookie ishlatsangiz,
HttpOnly,SecurevaSameSiteatributlarini albatta o'rnating.