5.15-bob: Autentifikatsiya — session, cookie, JWT, bcrypt
5-QISM — Node.js Backend · 15-mavzu
1. Kirish va motivatsiya
Endi backend'ning eng muhim, eng ko'p so'raladigan mavzusiga keldik: autentifikatsiya (authentication) — foydalanuvchi kimligini aniqlash. Har bir real ilovada bu bor: ro'yxatdan o'tish, login, "tizimga kirgan holda qolish", maxfiy sahifalar. Intervyularda ham, ishda ham — auth bilmasdan backend dasturchi bo'lib bo'lmaydi.
Bu mavzu — xavfsizlikning (14) markazi. Bitta xato — minglab foydalanuvchi paroli o'g'irlanishiga olib keladi (real falokat, yangiliklar sarlavhasi). Shuning uchun bu yerda "qanday ishlaydi" dan ham muhimrog'i — "nega aynan shunday, va nima xavfli" ni tushunish. Biz boshidan, eng sodda (va xato) yo'ldan boshlab, nega professional yechimlar (bcrypt, JWT, xavfsiz cookie) kerakligini bosqichma-bosqich ko'ramiz.
Asosiy ikki masala bor. Birinchi: parolni qanday xavfsiz saqlash (hech kim, hatto DB'ni ko'rgan dasturchi ham, parolni bilmasin) — bu bcrypt. Ikkinchi: HTTP holatsiz (stateless — 0.4) bo'lsa, foydalanuvchi qanday "kirgan holda qoladi" (har so'rovda qayta parol so'ramasdan) — bu session/cookie yoki JWT.
O'xshatish 1 (autentifikatsiya): auth — klubga kirish. Eshikda kimligingizni tasdiqlaysiz (pasport — login/parol). Tasdiqlangach, qo'lingizga bilaguzuk (cookie/token) bog'lashadi. Endi har safar barda yoki zalda pasport ko'rsatmaysiz — bilaguzuk yetarli (har so'rovda token). Bilaguzuk — "men allaqachon tekshirilganman" degan isbot.
O'xshatish 2 (hashing): parolni ochiq saqlash — uy kalitini eshik tagiga qo'yish (har kim oladi). Hashing — kalitni qaytmas ko'rinishga aylantirib saqlash: hatto o'g'ri DB'ni ko'rsa ham, asl parolni tiklay olmaydi.
Nega muhim?
- Har ilovada bor — login/ro'yxat har joyda; bu — asos.
- Xavfsizlik markazi (14) — xato qimmatga tushadi (parol o'g'irlash).
- Intervyu/ish talabi — auth — backend dasturchining majburiy bilimi.
- Keyingi bo'limlar — refresh token 5.16-bob, RBAC 5.17-bob, OTP 5.18-bob — hammasi shu asosga quriladi.
2. Nazariya — chuqur tushuntirish
2.1. Authentication vs Authorization (ikki xil "auth")
Ikki tushuncha tez-tez aralashtiriladi:
Authentication (autentifikatsiya) — "SIZ KIMSIZ?"
kimligini tasdiqlash (login + parol "ha, bu Ali")
Authorization (avtorizatsiya) — "SIZGA NIMAGA RUXSAT BOR?"
ruxsatlarni tekshirish (Ali admin'mi? bu sahifani ko'rolladimi?)Tartib: avval authentication (kimligini aniqlash — bu bob), keyin authorization (ruxsat — 5.17: RBAC). Avval "kim", keyin "nimaga haqli". Bu bobda authentication + uni saqlab turish (session/JWT).
2.2. Sodda (XATO) yo'l — nega ishlamaydi
Yangi dasturchi auth'ni shunday yozadi: parolni DB'ga ochiq (plain text) saqlash:
users jadvali:
| id | email | parol |
| 1 | ali@a.uz | parol123 | OCHIQ! (FALOKAT)Nega falokat (14): (1) DB'ni ko'rgan har kim (dasturchi, admin, hacker DB'ni o'g'irlasa) — barcha parollarni biladi. (2) Odamlar bir parolni ko'p joyda ishlatadi — bitta sizish, boshqa hisoblar ham xavf ostida. (3) Bu — qonun buzilishi (ma'lumot himoyasi). Parol hech qachon ochiq saqlanmaydi — bu eng asosiy qoida.
2.3. Hashing — yechim (bir tomonlama funksiya)
Hashing — ma'lumotni qaytmas (bir tomonlama) tarzda boshqa qiymatga aylantirish (5.3: crypto'da ko'rdik). Asl parolni saqlamaymiz — uning hashini saqlaymiz:
"parol123" ──hash──▶ "$2b$12$KIXxPf... (60 belgi)"
Hash'dan ortga "parol123"ni TIKLAB BO'LMAYDI (bir tomonlama)
Login'da: kiritilgan parolni HASH qilamiz, saqlangan hash bilan SOLISHTIRAMIZ.
Bir xil bo'lsa — parol to'g'ri (asl parolni hech qachon bilmaymiz!)Hashing vs shifrlash (encryption) farqi: shifrlash — ikki tomonlama (kalit bilan ortga ochiladi); parol uchun yaroqsiz (kalit sizsa — hammasi ochiladi). Hashing — bir tomonlama (ortga yo'q); parol uchun aynan shu kerak. Parolni hech qachon "ochib" ko'rmaymiz — faqat solishtramiz.
2.4. Oddiy hash yetarli emas — salt va rainbow table
Oddiy hash (MD5, SHA-256 — 5.3) parol uchun yetarli emas. Ikki sabab:
1. Bir xil parol bir xil hash: ikki odam "parol123" ishlatsa, hash bir xil. Hacker buni ko'radi.
2. Rainbow table (kamalak jadvali): hacker oldindan millionlab parol va ularning hash'ini hisoblab qo'ygan jadval. Hash'ni jadvaldan qidirib, parolni topadi (tez).
Yechim — salt (tuz): har parolga tasodifiy qiymat (salt) qo'shib, keyin hash qilish:
"parol123" + salt "xY9$" hash A
"parol123" + salt "qW2#" hash B (bir xil parol, LEKIN har xil hash!)
Rainbow table ishlamaydi (har parol noyob salt bilan)
Bir xil parollar — har xil hash (kim nima ishlatganini bilib bo'lmaydi)2.5. bcrypt — parol uchun maxsus (sekin — bu yaxshi)
bcrypt — parol hashlash uchun maxsus yaratilgan algoritm (authgear/owasp). Ikki muhim xususiyat:
- Salt'ni avtomatik ichiga oladi (hash'ning bir qismi sifatida — qo'lda boshqarmaysiz).
- Ataylab SEKIN — va sozlanadigan tezlik ("cost factor"). Bu — xavfsizlik xususiyati:
Oddiy hash (SHA): juda tez hacker soniyasiga MILLIONLAB parol sinaydi (brute-force)
bcrypt: ataylab sekin hacker soniyasiga juda KAM sinaydi (brute-force amalda imkonsiz)
Login bir marta bo'ladi (sekinlik sezilmaydi); hacker MILLIARD marta sinaydi (sekinlik to'sadi)Cost factor (rounds): bcrypt'ning sekinligi —
10,12kabi son bilan sozlanadi. Har +1 — ikki barobar sekinroq. 10–12 — 2026'da tavsiya (owasp). Kompyuterlar tezlashgani sayin, cost oshiriladi. argon2 — zamonaviy muqobil (yaxshiroq nazariy xususiyat); bcrypt — keng qo'llab-quvvatlangan, sinovdan o'tgan. Ikkalasi ham to'g'ri tanlov.
2.6. bcrypt hash tuzilishi (under the hood)
bcrypt hash — bitta satr, hamma narsa ichida:
$2b$12$KIXxPfH6.../EeF1qz...
│ │ │ │
│ │ │ └── hash (parol + salt natijasi)
│ │ └── salt (22 belgi — avtomatik)
│ └── cost factor (12 — rounds)
└── algoritm versiyasi ($2b$)
salt hash ichida! Solishtirishda bcrypt o'zi ajratib oladi.
Shuning uchun "salt'ni alohida saqlash" kerak emas (bcrypt o'zi qiladi).Nega
bcrypt.compare(===emas) — timing (14): parolni solishtirgandahash === saqlanganishlatib bo'lmaydi. Oddiy===belgilarni ketma-ket taqqoslaydi va birinchi farqda darhol to'xtaydi — natijada to'g'ri javob biroz uzoqroq ishlaydi. Hacker bu vaqt farqini (mikrosoniya) o'lchab, belgi-belgi parolni taxmin qilishi mumkin (timing attack).bcrypt.compare— doimiy vaqtli (constant-time): natija nima bo'lishidan qat'i nazar bir xil vaqt ishlaydi, vaqt sizib chiqmaydi. Shuning uchun parol/token solishtirishda doim maxsus funksiya (bcrypt.compare,crypto.timingSafeEqual— 5.3) ishlating.
2.7. Ro'yxatdan o'tish va login oqimi (umumiy rasm)
RO'YXATDAN O'TISH (register):
Foydalanuvchi: email + parol
1. validatsiya 5.9-bob — email to'g'ri, parol kuchli
2. email band emasligini tekshiring (DB — 6)
3. parolni HASH qiling (bcrypt — 2.5)
4. DB'ga saqlang (hash, ochiq parol EMAS)
LOGIN (kirish):
Foydalanuvchi: email + parol
1. email bo'yicha foydalanuvchini toping (DB)
2. kiritilgan parolni saqlangan hash bilan SOLISHTIRING (bcrypt.compare)
3. to'g'ri "kirgan" holatini bering (session/JWT — 2.8+)
4. noto'g'ri 401 (xato — 5.7)2.8. HTTP holatsiz — "kirgan holda qolish" muammosi
Asosiy muammo: HTTP holatsiz (stateless — 0.4). Har so'rov mustaqil — server oldingi so'rovni "eslamaydi". Login qildingiz, lekin keyingi so'rovda server sizni tanimaydi (yangi so'rov, yangi ulanish):
So'rov 1: login (email+parol) server: "to'g'ri, kirdingiz"
So'rov 2: profilni ber server: "siz kimsiz? (oldingisini eslamayman)"
Har so'rovda qayta parol so'rash MUMKIN EMAS (dahshatli UX)
Yechim: har so'rovga "men kirdim" ISBOTINI biriktirishIkki asosiy yechim: session (stateful — server eslaydi) va token/JWT (stateless — isbot o'zida).
2.9. Session-based auth (stateful — server eslaydi)
Session yondashuvi: login muvaffaqiyatli bo'lsa, server xotirasida/DB'da "sessiya" yozadi (kim, qachon) va unga noyob session ID beradi. Bu ID foydalanuvchiga cookie 2.10-bob orqali beriladi. Keyingi so'rovlarda cookie avtomatik keladi, server ID bo'yicha sessiyani topadi (loginradius):
Login server sessiya yaratadi:
session store: { "abc123": { userId: 7, ... } } server ESLAYDI (stateful)
foydalanuvchiga cookie: sessionId=abc123
Har so'rov cookie (sessionId=abc123) avtomatik keladi
server store'dan "abc123"ni topadi "ha, bu user 7" "Stateful" — server holatni saqlaydi (kim kirgan). Session store — xotira (kichik), yoki Redis 5.21-bob / DB (ko'p server uchun).
2.10. Cookie — nima va qanday ishlaydi
Cookie — server brauzerga beradigan kichik ma'lumot; brauzer uni saqlaydi va har so'rovda avtomatik qaytaradi 0.4-bob:
Server javobida: Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax
Brauzer saqlaydi va har so'rovda: Cookie: sessionId=abc123 (avtomatik!)Cookie'ning kuchi — avtomatik yuboriladi (qo'lda boshqarmaysiz); brauzer domenga bog'lab saqlaydi. Session ID, token shu yerda saqlanadi. Lekin xavfsiz sozlanishi shart 2.11-bob.
2.11. Cookie xavfsizligi — HttpOnly, Secure, SameSite (14)
Cookie'ning xavfsizlik atributlari — eng muhim (MDN, 14):
HttpOnly — JavaScript cookie'ni O'QIY OLMAYDI (document.cookie)
XSS hujumi cookie'ni o'g'irlay olmaydi (14)
Secure — faqat HTTPS orqali yuboriladi (shifrlangan — 14)
tarmoqda ochiq ketmaydi
SameSite — cookie qaysi so'rovlarda yuboriladi:
Strict — faqat o'z saytdan; Lax — asosan o'z saytdan (default, tavsiya);
None — har joydan (Secure bilan birga)
CSRF hujumiga qarshi himoya (14)Eng muhim qoida (14): auth cookie'si
HttpOnlybo'lsin (JS o'qiy olmasin — XSS himoyasi),Secure(production — HTTPS),SameSite=Lax(CSRF himoyasi). Bu uchtasi — 2026 standart himoyasi. Token'nilocalStorageda saqlash — XSS uchun ochiq 2.15-bob.
CSRF nima (14): Cross-Site Request Forgery — boshqa sayt sizning nomingizdan so'rov yuborishi. Cookie avtomatik yuborilgani uchun 2.10-bob xavf shu yerda: agar foydalanuvchi
bank.uz'da kirgan bo'lsa va aldab boshqa (yomon.uz) saytga kiritilsa, o'sha sahifa yashirinbank.uz'ga so'rov yuborsa — brauzer cookie'ni avtomatik biriktiradi, server "haqiqiy foydalanuvchi" deb qabul qiladi.SameSite=Laxaynan shuni to'sadi: cookie faqat o'z saytdan kelgan so'rovlarga qo'shiladi. Diqqat: CSRF — cookie-auth muammosi;Authorizationheader'da token yuborilsa (qo'lda qo'shiladi, avtomatik emas), CSRF tabiiy yo'q.
2.12. Token-based auth (stateless — isbot o'zida)
Ikkinchi yondashuv — token (asosan JWT). Login'da server imzolangan token beradi; bu token o'zida foydalanuvchi ma'lumotini va imzo'ni saqlaydi. Server hech narsa eslamaydi — har so'rovda tokenni tekshiradi (stateless — descope):
Login server JWT yasaydi (imzolangan): "user 7, admin, muddati 1soat"
foydalanuvchiga token beradi
server HECH NARSA SAQLAMAYDI (stateless)
Har so'rov token keladi (header/cookie)
server imzoni TEKSHIRADI ishonchli bo'lsa "bu user 7"
(store'ga qaramaydi — token o'zi yetarli)"Stateless" — server holat saqlamaydi; token o'zi ishonchli isbot (imzo bilan). Ko'p serverga oson masshtablanadi (session store kerak emas — har server tokenni o'zi tekshiradi).
2.13. JWT tuzilishi — header.payload.signature (under the hood)
JWT (JSON Web Token) — uch qismdan iborat, nuqta bilan ajratilgan satr (jwt.io):
xxxxx.yyyyy.zzzzz
│ │ │
│ │ └── SIGNATURE (imzo) — maxfiy kalit bilan; o'zgartirishni aniqlaydi
│ └── PAYLOAD — ma'lumot (userId, rol, muddat) — base64 (OCHIQ o'qiladi!)
└── HEADER — algoritm va tur (base64)
Misol:
eyJhbGc... (header: {"alg":"HS256","typ":"JWT"})
eyJzdWI... (payload: {"sub":7,"rol":"admin","exp":1700000000})
SflKxwRJ... (signature)Muhim (14): payload shifrlangan EMAS — faqat base64 (har kim o'qiydi!). Maxfiy ma'lumot (parol, karta) JWT'ga solmang. JWT'ning kuchi — maxfiylikda emas, yaxlitlikda: imzo o'zgartirishni aniqlaydi (kimdir payload'ni o'zgartirsa — imzo mos kelmaydi, rad etiladi).
2.14. JWT imzolash va tekshirish (HS256 vs RS256)
Imzo maxfiy kalit bilan yaratiladi va tekshiriladi:
HS256 (simmetrik) — bitta MAXFIY kalit imzolaydi VA tekshiradi
sodda; bir server/monolit uchun (kalit hammada bo'lishi kerak)
RS256 (asimmetrik) — PRIVATE kalit imzolaydi, PUBLIC kalit tekshiradi
mikroservis uchun xavfsizroq (auth servis imzolaydi, boshqalar
faqat public bilan tekshiradi — private faqat bitta joyda — 14)Tanlov: monolit (bitta backend) — HS256 (sodda,
JWT_SECRET— 5.8). Mikroservis 9.9-bob yoki tashqi tekshirish — RS256 (auth.com/workos tavsiyasi).JWT_SECRETkuchli bo'lsin (kamida 32 belgi —crypto.randomBytes— 5.3, 5.8).
2.15. Tokenni qayerda saqlash (cookie vs localStorage — 14)
Frontend (11) tokenni qayerda saqlaydi?
localStorage — JS o'qiydi XSS hujumi o'g'irlaydi (14) (xavfli)
httpOnly cookie — JS o'qiy olmaydi XSS himoyasi (14) (tavsiya)2026 tavsiya (14): tokenni httpOnly cookie'da saqlang (
localStorageda emas — workos/authgear).localStorage— har qanday XSS skripti o'qiydi (token o'g'irlanadi). httpOnly cookie — JS ko'rmaydi. Cookie ishlatsangiz — CSRF himoyasi ham kerak (SameSite— 2.11).
2.16. Session vs JWT — taqqoslash (qachon qaysi)
| Xususiyat | Session (stateful) | JWT (stateless) |
|---|---|---|
| Server saqlaydi | Ha (store/Redis) | Yo'q |
| Masshtab (ko'p server) | Shared store kerak | Oson (kalit yetarli) |
| Bekor qilish (logout) | Oson (store'dan o'chir) | Qiyin (muddat tugaguncha amal qiladi) |
| Ma'lumot | Server'da | Token ichida |
| Mikroservis | Murakkabroq | Mos |
Tavsiya (clerk/auth0): ko'p ilova uchun — qisqa muddatli JWT (access — 15 daqiqa) + refresh token (uzoq — 5.16) gibrid yondashuvi: JWT tezligi + bekor qilish imkoni. Klassik server-rendered ilova — session. Ikkalasi ham to'g'ri; loyihaga qarab.
2.17. JWT muammosi — bekor qilish va muddat (5.16 ko'prik)
JWT'ning kamchiligi: bekor qilib bo'lmaydi (stateless — server eslamaydi). Token o'g'irlansa yoki foydalanuvchi logout qilsa ham, token muddati tugaguncha amal qiladi:
Yechim:
1. Qisqa muddat (access token 15 daqiqa — exp) o'g'irlansa, tez tugaydi
2. Refresh token (uzoq, DB'da — bekor qilinadi) yangi access olish 5.16-bob
3. Blacklist (Redis — bekor qilingan tokenlar — 5.21)Bu — 5.16-bob (Access/Refresh tokens) mavzusi. Hozir asosni (bitta JWT) tushunamiz; keyingi bobda professional token strategiyasini quramiz.
2.18. Xavfsizlik amaliyotlari (14)
Parolni bcrypt/argon2 bilan hash (cost 10-12 — 2.5); ochiq saqlamang 2.2-bob
Login'da xato xabari UMUMIY ("email yoki parol noto'g'ri" — qaysi xato ekanin oshkor qilmang — 14)
JWT_SECRET kuchli (32+ belgi, .env — 5.8); RS256 mikroservisda 2.14-bob
Token httpOnly cookie'da (localStorage emas — 2.15)
Cookie: HttpOnly + Secure + SameSite 2.11-bob
Login'ga rate limiting (brute-force — 5.20, 14)
Parol kuchliligini talab qiling (validatsiya — 5.9)
Qisqa access token + refresh 5.16-bob; HTTPS (14)
Maxfiy ma'lumotni JWT payload'ga solmang (2.13)3. Sintaksis — tez ma'lumotnoma
// bcrypt (2.5)
import bcrypt from "bcrypt";
const hash = await bcrypt.hash(parol, 12); // hashlash (cost 12)
const togri = await bcrypt.compare(parol, hash); // solishtirish true/false
// JWT (2.13)
import jwt from "jsonwebtoken";
const token = jwt.sign({ sub: user.id, rol }, SECRET, { expiresIn: "1h" }); // yasash
const payload = jwt.verify(token, SECRET); // tekshirish (xato throw)
// Cookie 2.11-bob — Express
res.cookie("token", token, { httpOnly: true, secure: true, sameSite: "lax", maxAge: 3600000 });
res.clearCookie("token"); // logout
// Session 2.9-bob — express-session
app.use(session({ secret, resave: false, saveUninitialized: false, store }));
req.session.userId = user.id; // saqlash4. Batafsil kod namunalari
Misol 1 — bcrypt: hash va solishtirish (2.5)
import bcrypt from "bcrypt"; // npm install bcrypt
// HASHLASH (ro'yxatdan o'tishda — 2.7)
const parol = "MaxfiyParol123";
const hash = await bcrypt.hash(parol, 12); // cost 12 (2.5)
console.log(hash); // "$2b$12$KIXx..." (60 belgi — salt ichida, 2.6)
// SOLISHTIRISH (login'da — 2.7)
const togri = await bcrypt.compare("MaxfiyParol123", hash); // true
const xato = await bcrypt.compare("BoshqaParol", hash); // false
// bcrypt.compare hash'dan salt'ni o'zi ajratadi 2.6-bob — qo'lda salt kerak emasMisol 2 — Ro'yxatdan o'tish (register — 2.7)
import bcrypt from "bcrypt";
import { z } from "zod"; // validatsiya (5.9)
const signupSchema = z.object({
email: z.string().trim().toLowerCase().email(),
parol: z.string().min(8).regex(/[A-Z]/, "Katta harf").regex(/\d/, "Raqam"), // (5.9)
}).strict();
export const signup = async (req, res, next) => {
try {
// 1. Validatsiya (5.9)
const { email, parol } = signupSchema.parse(req.body);
// 2. Email band emasligini tekshiring (DB — 6)
const mavjud = await User.findOne({ email });
if (mavjud) return res.status(409).json({ error: "Email band" }); // 409 (5.7)
// 3. Parolni HASH qiling 2.5-bob — ochiq saqlamang!
const parolHash = await bcrypt.hash(parol, 12);
// 4. DB'ga saqla (hash, ochiq parol EMAS — 2.2)
const user = await User.create({ email, parol: parolHash });
// 5. Javob (parolni QAYTARMA!)
res.status(201).json({ id: user.id, email: user.email });
} catch (err) {
next(err); // (5.10)
}
};Misol 3 — Login (kirish + JWT — 2.7, 2.13)
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { config } from "../config/index.js"; // (5.8)
export const login = async (req, res, next) => {
try {
const { email, parol } = req.body; // (validatsiya — 5.9)
// 1. Foydalanuvchini top (DB — 6)
const user = await User.findOne({ email: email.toLowerCase() });
// 2. UMUMIY xato (email yo'q/parol xato — qaysiligini oshkor qilmang! — 2.18, 14)
if (!user) return res.status(401).json({ error: "Email yoki parol noto'g'ri" });
// 3. Parolni solishtir (2.5)
const togri = await bcrypt.compare(parol, user.parol);
if (!togri) return res.status(401).json({ error: "Email yoki parol noto'g'ri" });
// 4. JWT yasash 2.13-bob — imzolangan
const token = jwt.sign(
{ sub: user.id, rol: user.rol }, // payload (maxfiy emas — 2.13)
config.jwt.secret, // maxfiy kalit (5.8)
{ expiresIn: "1h" } // muddat (2.17)
);
// 5. Token'ni httpOnly cookie'da ber (2.15)
res.cookie("token", token, {
httpOnly: true, // JS o'qiy olmaydi (2.11, 14)
secure: config.isProd, // production HTTPS (2.11)
sameSite: "lax", // CSRF himoyasi (2.11)
maxAge: 60 * 60 * 1000, // 1 soat
});
res.json({ user: { id: user.id, email: user.email, rol: user.rol } });
} catch (err) {
next(err);
}
};Misol 4 — Auth middleware (himoyalangan route — 2.12)
import jwt from "jsonwebtoken";
import { config } from "../config/index.js";
// Tokenni tekshiruvchi middleware (5.6)
export const auth = (req, res, next) => {
// Token'ni cookie'dan yoki Authorization header'dan ol (2.15)
const token = req.cookies?.token ||
(req.headers.authorization?.startsWith("Bearer ") &&
req.headers.authorization.slice(7));
if (!token) return res.status(401).json({ error: "Avtorizatsiya kerak" }); // (5.7)
try {
const payload = jwt.verify(token, config.jwt.secret); // tekshir (2.14)
req.user = { id: payload.sub, rol: payload.rol }; // so'rovga biriktir
next(); // ruxsat (5.6)
} catch {
return res.status(401).json({ error: "Token noto'g'ri yoki muddati tugagan" });
}
};
// Ishlatish — himoyalangan route (5.6)
app.get("/api/profile", auth, (req, res) => {
res.json({ userId: req.user.id }); // req.user — auth'dan (ishonchli)
});Misol 5 — Cookie'ni o'qish (cookie-parser — 2.10)
import cookieParser from "cookie-parser"; // npm install cookie-parser
app.use(cookieParser()); // cookie'larni req.cookies'ga (5.6)
app.get("/api/me", (req, res) => {
const token = req.cookies.token; // cookie'dan o'qish (2.10)
if (!token) return res.status(401).json({ error: "Kirilmagan" });
// ... tekshirish (Misol 4) ...
});Misol 6 — Logout (cookie tozalash — 2.11)
export const logout = (req, res) => {
// Cookie'ni o'chir (sozlamalar mos bo'lishi kerak — 2.11)
res.clearCookie("token", {
httpOnly: true,
secure: config.isProd,
sameSite: "lax",
});
res.json({ message: "Tizimdan chiqdingiz" });
};
// JWT stateless — logout faqat cookie'ni o'chiradi; token muddatigacha amal qiladi (2.17)
// To'liq bekor qilish uchun — refresh token + blacklist (5.16)Misol 7 — JWT'ni qo'lda tekshirish (under the hood — 2.13)
import jwt from "jsonwebtoken";
const token = jwt.sign({ sub: 7, rol: "admin" }, "secret", { expiresIn: "1h" });
// Payload — IMZOSIZ ham o'qiladi (base64 — 2.13)! Maxfiy emas
const ochiq = jwt.decode(token); // tekshirmasdan o'qish
console.log(ochiq); // { sub: 7, rol: "admin", iat: ..., exp: ... }
// Imzo bilan TEKSHIRISH (xavfsiz — 2.14)
try {
const togri = jwt.verify(token, "secret"); // imzo to'g'rimi?
console.log("Ishonchli:", togri.sub);
} catch (err) {
console.log("Soxta/muddati tugagan:", err.message); // TokenExpiredError / JsonWebTokenError
}
// jwt.decode — faqat o'qish (tekshirmaydi!); jwt.verify — IMZONI tekshiradi (2.13)Misol 8 — Session-based auth (express-session — 2.9)
import session from "express-session"; // npm install express-session
import { RedisStore } from "connect-redis"; // production store (5.21)
app.use(session({
store: new RedisStore({ client: redisClient }), // Redis store (ko'p server — 2.9)
secret: config.session.secret, // cookie imzosi (5.8)
resave: false,
saveUninitialized: false,
cookie: { httpOnly: true, secure: config.isProd, sameSite: "lax", maxAge: 86400000 }, // (2.11)
}));
// Login — sessiyaga yoz (2.9)
app.post("/login", async (req, res) => {
const user = await User.findOne({ email: req.body.email });
if (!user || !(await bcrypt.compare(req.body.parol, user.parol))) {
return res.status(401).json({ error: "Email yoki parol noto'g'ri" }); // (2.18)
}
req.session.userId = user.id; // server ESLAYDI (stateful — 2.9)
res.json({ message: "Kirdingiz" });
});
// Himoyalangan — sessiyadan o'qi
app.get("/profile", (req, res) => {
if (!req.session.userId) return res.status(401).json({ error: "Kirilmagan" });
res.json({ userId: req.session.userId });
});
// Logout — sessiyani o'chir (oson bekor qilish — 2.16)
app.post("/logout", (req, res) => {
req.session.destroy(() => res.json({ message: "Chiqdingiz" }));
});Misol 9 — User modelida parol himoyasi (DB — 6)
// Mongoose model (6) — parolni hech qachon JSON'da qaytarmang
const userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
parol: { type: String, required: true, select: false }, // default'da QAYTARILMAYDI (14)
rol: { type: String, enum: ["user", "admin"], default: "user" },
});
// Saqlashdan oldin avtomatik hash (pre-save hook — 6)
userSchema.pre("save", async function (next) {
if (!this.isModified("parol")) return next(); // faqat o'zgarganda
this.parol = await bcrypt.hash(this.parol, 12); // (2.5)
next();
});
// Solishtirish metodi
userSchema.methods.parolToOgri = function (parol) {
return bcrypt.compare(parol, this.parol); // (2.5)
};Misol 10 — To'liq auth router (2.7)
import { Router } from "express";
import { auth } from "../middleware/auth.js";
const router = Router();
router.post("/signup", signup); // ro'yxat (Misol 2)
router.post("/login", login); // login (Misol 3)
router.post("/logout", logout); // logout (Misol 6)
router.get("/me", auth, async (req, res) => { // himoyalangan (Misol 4)
const user = await User.findById(req.user.id); // (6)
res.json({ id: user.id, email: user.email, rol: user.rol });
});
export default router;Misol 11 — Parol o'zgartirish (xavfsiz — 2.5)
export const parolOzgartir = async (req, res, next) => {
try {
const { eskiParol, yangiParol } = req.body; // (validatsiya — 5.9)
const user = await User.findById(req.user.id).select("+parol"); // parol bilan (Misol 9)
// 1. Eski parolni tasdiqla (2.5)
if (!(await bcrypt.compare(eskiParol, user.parol))) {
return res.status(401).json({ error: "Eski parol noto'g'ri" });
}
// 2. Yangi parolni hash qiling va saqlang
user.parol = await bcrypt.hash(yangiParol, 12); // (2.5)
await user.save();
res.json({ message: "Parol o'zgartirildi" });
// Production: barcha eski tokenlarni bekor qiling (5.16)
} catch (err) { next(err); }
};Misol 12 — Login'ga rate limiting (brute-force himoyasi — 2.18, 14)
import rateLimit from "express-rate-limit"; // (5.20)
// Login endpoint'iga maxsus chegara (brute-force'ga qarshi — 14)
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 daqiqa
max: 5, // 5 urinish
message: { error: "Juda ko'p urinish. 15 daqiqadan keyin urinib ko'ring" },
standardHeaders: true,
});
app.post("/api/auth/login", loginLimiter, login); // (Misol 3)
// hacker parolni cheksiz sinab ko'rolmaydi (5.20'da chuqur)5. To'g'ri va noto'g'ri holatlar
1) Parolni ochiq saqlash
// FALOKAT — ochiq parol (14, 2.2)
await User.create({ email, parol });
// hash (bcrypt)
await User.create({ email, parol: await bcrypt.hash(parol, 12) });2) Login xatosida qaysi maydon xato ekanini aytish
// hacker'ga ma'lumot ("email yo'q" email mavjudligini bilib oladi — 14, 2.18)
if (!user) return res.json({ error: "Bunday email yo'q" });
// umumiy xabar
return res.status(401).json({ error: "Email yoki parol noto'g'ri" });3) Tokenni localStorage'da saqlash
localStorage XSS o'g'irlaydi (14, 2.15)
httpOnly cookie4) Maxfiy ma'lumotni JWT payload'ga solish
// payload base64 — har kim o'qiydi (14, 2.13)
jwt.sign({ parol: user.parol, karta: "..." }, secret);
// faqat ID/rol (maxfiy emas)
jwt.sign({ sub: user.id, rol: user.rol }, secret);5) Zaif JWT_SECRET
// oson topiladigan kalit (14)
jwt.sign(payload, "secret123");
// kuchli, .env'da (crypto.randomBytes — 5.3, 5.8)
jwt.sign(payload, config.jwt.secret); // 32+ belgi6) Cookie'ni xavfsizlik flag'larisiz
// HttpOnly/Secure/SameSite yo'q (14, 2.11)
res.cookie("token", token);
// to'liq himoya
res.cookie("token", token, { httpOnly: true, secure: true, sameSite: "lax" });6. Keng tarqalgan xatolar va yechimlari
Xato 1 — bcrypt.compare doim false
Sababi: hash saqlanmagan (ustun qisqa — DB), yoki ikki marta hash qilingan (pre-save hook + qo'lda — Misol 9). Yechimi: ustun uzunligini tekshiring (60+); bir marta hash.
Xato 2 — JsonWebTokenError: invalid signature
Sababi: verify'dagi kalit sign'dagidan farq qiladi 5.8-bob. Yechimi: bir xil JWT_SECRET; .env to'g'ri yuklanganmi.
Xato 3 — TokenExpiredError
Sababi: token muddati tugagan (expiresIn — 2.17). Yechimi: refresh token bilan yangilash 5.16-bob; qayta login.
Xato 4 — Cookie kelmaydi (frontend so'rovida)
Sababi: CORS credentials yo'q, yoki frontend fetch'da credentials: "include" yo'q 5.20-bob. Yechimi: server cors({ credentials: true, origin }); frontend credentials: "include".
Xato 5 — req.cookies undefined
Sababi: cookie-parser middleware yo'q (Misol 5). Yechimi: app.use(cookieParser()).
Xato 6 — Parol javobda qaytyapti
Sababi: model parolni qaytaradi (Misol 9). Yechimi: select: false; javobda parolni olib tashlang.
Xato 7 — SameSite cookie cross-site kelmaydi
Sababi: frontend va backend turli domen; SameSite=Lax/Strict bloklaydi. Yechimi: SameSite=None + Secure (HTTPS); yoki bir domen (proxy).
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- crypto 5.3-bob: hashing asosi; JWT_SECRET yaratish.
- Express 5.6-bob: auth middleware, route himoyasi.
- Env 5.8-bob: JWT_SECRET, session secret.
- Validatsiya 5.9-bob: email/parol; parol kuchliligi.
- Error handling 5.10-bob: 401/409; umumiy xato.
- Refresh tokens 5.16-bob: bu asosni davom ettiradi.
- RBAC 5.17-bob: auth'dan keyin avtorizatsiya (req.user.rol).
- OTP/SMS 5.18-bob: ikki bosqichli; telefon auth.
- Rate limiting 5.20-bob: login brute-force himoyasi.
- Redis 5.21-bob: session store; token blacklist.
- DB (6): user model, parol hash.
- Frontend (11): login forma, cookie/token, himoyalangan route.
- NestJS 8.16-bob: Passport, JWT strategy, guards — shu g'oya.
8. Eng yaxshi amaliyotlar (best practices)
- Parolni bcrypt/argon2 bilan hash (cost 10-12 — 2.5); ochiq saqlamang 2.2-bob.
- Login xatosi UMUMIY ("email yoki parol noto'g'ri" — 2.18, 14).
- Token httpOnly cookie'da (localStorage emas — 2.15, 14).
- Cookie: HttpOnly + Secure + SameSite=Lax (2.11, 14).
- JWT_SECRET kuchli (32+ belgi, .env — 5.8); RS256 mikroservisda 2.14-bob.
- Maxfiy ma'lumotni JWT payload'ga solmang (base64 ochiq — 2.13).
- Qisqa access token (15min-1soat) + refresh (5.16, 2.17).
- Login'ga rate limiting (brute-force — 5.20, Misol 12).
- Parol kuchliligini talab qiling (validatsiya — 5.9).
- Parolni javobda qaytarmang (select: false — Misol 9).
- HTTPS (production — 14); logout (cookie tozalash + refresh bekor — 5.16).
- Session uchun Redis store (ko'p server — 2.9, 5.21).
9. Amaliy loyiha: "To'liq Autentifikatsiya Tizimi"
Autentifikatsiyani professional, xavfsiz darajada mustahkamlash.
Maqsad
bcrypt, JWT va xavfsiz cookie bilan to'liq auth tizimini qurish: ro'yxatdan o'tish, login, himoyalangan route'lar, logout va parol o'zgartirish — xavfsizlik amaliyotlari bilan.
Talablar (requirements)
- Ro'yxatdan o'tish: validatsiya (email, kuchli parol — 5.9); email band tekshiruvi; bcrypt hash; parolni qaytarmaslik (Misol 2, 2.7).
- Login: parolni
bcrypt.compare; UMUMIY xato xabari; JWT yasash; httpOnly cookie (Misol 3, 2.18). - Auth middleware: token tekshiring (cookie/header);
req.user; himoyalangan route'lar (Misol 4, 2.12). - Logout: cookie tozalash (Misol 6, 2.11).
- Profil (/me): himoyalangan; joriy foydalanuvchi ma'lumoti (Misol 10).
- Parol o'zgartirish: eski parolni tasdiqlash; yangi hash (Misol 11).
- Cookie xavfsizligi: HttpOnly + Secure + SameSite (2.11, 14).
- JWT_SECRET: .env'da, kuchli (crypto.randomBytes — 5.3, 5.8).
- Rate limiting: login endpoint'iga (brute-force — Misol 12, 5.20).
- User model: parol
select: false; pre-save hash (Misol 9, 6). - (Bonus) Session varianti: express-session + Redis bilan ham yozib, JWT bilan solishtir (Misol 8, 2.16).
Maslahatlar (hint)
bcrypt.hash(parol, 12)/bcrypt.compare2.5-bob.- Login xato: qaysi maydon xato ekanini aytmang (2.18, 2-xato).
- Cookie:
{ httpOnly: true, secure: isProd, sameSite: "lax" }2.11-bob. cookie-parsermiddleware (5-xato); CORScredentials(4-xato).- JWT:
jwt.sign({ sub, rol }, secret, { expiresIn })2.13-bob; maxfiy ma'lumot solmang. - Parolni javobdan olib tashlang (select: false — 6-xato).
"Tayyor" mezonlari (acceptance criteria)
- Ro'yxatdan o'tish: parol hash bo'lib saqlanadi (ochiq emas).
- Login: to'g'ri parolda JWT cookie beriladi; xato umumiy.
- Auth middleware himoyalangan route'larni qo'riqlaydi.
- Logout cookie'ni tozalaydi.
- /me joriy foydalanuvchini qaytaradi.
- Parol o'zgartirish eski parolni tasdiqlaydi.
- Cookie HttpOnly+Secure+SameSite bilan.
- JWT_SECRET .env'da, kuchli.
- Login rate limiting bilan himoyalangan.
- Parol hech qachon javobda qaytmaydi.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda backend'ning yuragini — autentifikatsiyani — chuqur o'rgandik:
- Authentication (kim siz?) vs authorization (nimaga haqlisiz? — 5.17 — 2.1).
- Parolni hech qachon ochiq saqlamang 2.2-bob — hashing (bir tomonlama — 2.3); salt (rainbow table — 2.4); bcrypt (sekin, cost 10-12, salt ichida — 2.5, 2.6).
- HTTP holatsiz (2.8) session (stateful, server eslaydi — 2.9) yoki JWT (stateless, isbot o'zida — 2.12).
- Cookie (avtomatik — 2.10); xavfsizlik (HttpOnly+Secure+SameSite — 2.11, 14).
- JWT tuzilishi (header.payload.signature; payload OCHIQ — 2.13); imzo (HS256/RS256 — 2.14); httpOnly cookie'da sakla 2.15-bob.
- Session vs JWT 2.16-bob; JWT bekor qilish muammosi refresh token 2.17-bob; xavfsizlik amaliyotlari (2.18, 14).
Keyingi bob — 5.16-bob: Access/Refresh tokens, token strategiyasi. Bitta JWT'ning kamchiligini (bekor qilib bo'lmaydi, muddat muammosi — 2.17) ko'rdik. Endi professional token strategiyasini quramiz: qisqa muddatli access token + uzoq muddatli refresh token, token rotatsiyasi, xavfsiz saqlash va bekor qilish. Bu — zamonaviy auth'ning standart yechimi.
Foydalanilgan rasmiy/ishonchli manbalar
- WorkOS / Authgear — Node.js authentication best practices 2026 (bcrypt, JWT, cookie)
- MDN — Secure cookie configuration (HttpOnly, Secure, SameSite); jwt.io — JWT struktura
- LoginRadius / Descope — Session vs Token (stateful vs stateless); OWASP — parol hashlash
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!