Mundarija (26)
- Bu darsda
- 1. Nega bu kerak?
- 2. Parolni saqlash
- 2.1 Nega SHA-256 emas
- 2.2 Kanon o'lchovi
- 2.3 scrypt bilan saqlash va tekshirish
- 2.4 Tuzoq: maxmem
- 2.5 argon2 Node 24 da
- 3. Shifrlash: AES-256-GCM
- 3.1 Uch tarkibiy qism
- 3.2 IV qoidasi
- 3.3 Paroldan kalit
- 4. Imzo: Ed25519
- 4.1 Asimmetrik kalitlar
- 4.2 Web Crypto: ECDSA P-256
- 5. Uch vazifa — bitta jadval
- 6. Hujumchi nigohi
- 7. CLI qadami o'rniga: nega valyuta ga kirmadi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): qaysi vosita?
- 2-mashq (o'rta): shifrni matn sifatida saqlash
- 3-mashq (qiyin): parametr yangilanishi
- 10. Real ishda
- Xulosa
- Manbalar
Node.js crypto 2: parolni hash'lash, shifrlash va imzo (AES-GCM, Ed25519)
Qisqacha: Uch xil vazifa — uch xil vosita. Parol saqlanmaydi, uning sekin hash'i saqlanadi: scrypt yoki argon2id, har foydalanuvchiga alohida tasodifiy tuz (salt) bilan. Node 24 da ikkalasi ham o'rnatilgan (
crypto.scrypt;crypto.argon2— 24.7 dan). Ma'lumotni yashirish — AES-256-GCM: 32 baytlik kalit, har safar yangi 12 baytlik IV va auth tag — buzilgan shifr ochilmaydi, xato beradi. "Buni men yubordim" degan isbot — ochiq kalitli imzo (Ed25519): yopiq kalit bilan imzolanadi, ochiq kalit bilan har kim tekshiradi.
Bu darsda
- Parolni nega oddiy hash bilan saqlab bo'lmasligini va scrypt/argon2 qanday himoya qilishini o'lchovlar bilan tushuntirasiz.
- Parolni tuz bilan hash qilib saqlaydigan va tekshiradigan kod yozasiz.
- AES-256-GCM bilan shifrlaysiz va IV, auth tag nima uchun kerakligini bilasiz.
- Ed25519 bilan imzolab tekshirasiz; simmetrik va asimmetrik kalit farqini bilasiz.
- Web Crypto'da kalit juftligi yaratasiz (web push uchun ECDSA
P-256).
Oldin bilishingiz kerak: crypto 1: tasodif, hash va HMAC, Buffer (qisqa eslatma pastda), Web push bildirishnomalar.
1. Nega bu kerak?
O'tgan darsda hash va HMAC'ni ko'rdik. Ikkalasi ham "o'zgarmagan" degan savolga javob beradi. Bugun uch yangi savol:
- Saytingiz bazasi o'g'irlandi. Unda 10 000 foydalanuvchining paroli bor. Hujumchi ularni o'qiy oladimi? Ko'pchilik bir parolni hamma joyda ishlatadi — bitta o'g'irlik ularning Telegram, pochta va bank hisoblarini ham ochishi mumkin.
- Faylni faqat egasi o'qisin. Masalan,
valyutasozlamasida kelajakda pullik API kaliti bo'lsa — diskda ochiq yotmasin. - Faylni men yuborganimni hamma tekshira olsin.
@login/valyutapaketingiz arxivini (login-valyuta-1.0.0.tgz) yuklagan odam uni aynan siz yasaganingizga ishonch hosil qilmoqchi — lekin u bilan sir bo'lishishingiz kerak emas.
Har biriga boshqa vosita kerak. Ularni chalkashtirish — real loyihalardagi eng og'ir xavfsizlik xatolaridan.
O'xshatish: uch xil qulf. Parol hash — barmoq izi skaneri: barmoqni saqlamaydi, faqat solishtiradi. Shifrlash — seyf: kaliti bor odam ochadi. Imzo — muhr: muhrni faqat egasi bosadi, lekin muhr namunasini hamma ko'rib solishtiradi.
valyuta ga bularning hech biri kerak emas: unda parol, sir yoki imzo yo'q. Shuning uchun bu dars — kanon tajribasi: o'lchovlar valyuta loyihasidan tashqarida qilingan.
2. Parolni saqlash
2.1 Nega SHA-256 emas
SHA-256 ataylab tez qilib yaratilgan: fayl va xabarlarni tez tekshirish uchun. Parol uchun bu kamchilik. Hujumchi bazani olgach, mashhur parollar ro'yxatini (123456, parol123, toshkent2026 …) bittalab hash qiladi va solishtiradi. Bitta zamonaviy videokarta sekundiga milliardlab SHA-256 hisoblaydi — oddiy parollar daqiqalarda topiladi.
Ikkinchi muammo: bir xil parol — bir xil hash. Hujumchi bitta hisob bilan minglab foydalanuvchini ochadi. Oldindan hisoblangan "parol → hash" jadvallari (rainbow table) ham shu yerda ishlaydi.
Ikkala muammoning davosi:
- Tuz (salt) — har foydalanuvchi uchun tasodifiy 16 bayt, parolga qo'shib hash qilinadi va hash yonida ochiq saqlanadi. Bir xil parol — har xil hash. Tayyor jadvallar ishlamaydi.
- Sekin hash — KDF (key derivation function, kalit hosil qilish funksiyasi): bitta hisob ataylab o'nlab-yuzlab millisekund va o'nlab megabayt xotira oladi. Foydalanuvchi kirishda 50 ms ni sezmaydi. Milliard urinish esa yillarga cho'ziladi.
2.2 Kanon o'lchovi
Kanon uch algoritmni bir xil parol bilan o'lchadi (Node 24.21.0, i5-12500H, Windows 11):
scrypt N=2^17 r=8 p=1: 286 ms
pbkdf2 sha256 600 000: 107 ms
crypto.argon2Sync bormi: function
argon2id m=19 MiB t=2 p=1: 31 ms- argon2id m=19 MiB t=2OWASP minimal31 ms
- pbkdf2-sha256 600 000OWASP tavsiyasi107 ms
- scrypt N=2^17 r=8128 MiB xotira286 ms
Manba: IlmHamroh kanon tajribasi t21-crypto2: Node 24.21.0, Windows 11, i5-12500H, 2026-10-23
Rasmni to'g'ri o'qing: argon2id eng tez chiqdi, lekin bu "eng zaif" degani emas. Uning kuchi — xotira: har urinish 19 MiB oladi. Videokarta minglab yadroga ega, lekin har yadroga 19 MiB berolmaydi — parallel hujum qimmatlashadi. pbkdf2 esa xotirani deyarli ishlatmaydi, uni videokartada parallel buzish arzon. Shuning uchun OWASP tavsiyasi tartibi: argon2id → scrypt → bcrypt → pbkdf2 (oxirgisi — faqat FIPS talab qilinganda).
Parametrlar — OWASP Password Storage Cheat Sheet'dagi minimal qiymatlar: argon2id uchun m=19 MiB, t=2, p=1; pbkdf2-sha256 uchun 600 000 iteratsiya. Serveringizda bitta hash 50–500 ms orasida bo'lsin — parametrlarni shunga moslang.
2.3 scrypt bilan saqlash va tekshirish
Node'da scrypt ancha yillardan beri bor. To'liq misol: hash qilish, saqlash formati, tekshirish. Tez ishlashi uchun N kichikroq (2^14) — haqiqiy serverda kattaroq:
import { randomBytes, scrypt, timingSafeEqual } from "node:crypto";
import { promisify } from "node:util";
const scryptP = promisify(scrypt);
const N = 2 ** 14;
async function parolniHashla(parol) {
const tuz = randomBytes(16);
const hash = await scryptP(parol, tuz, 32, { N });
const b64 = (b) => b.toString("base64url");
return `scrypt$${N}$${b64(tuz)}$${b64(hash)}`;
}
async function parolniTekshir(parol, saqlangan) {
const [, n, tuz64, hash64] = saqlangan.split("$");
const kutilgan = Buffer.from(hash64, "base64url");
const tuz = Buffer.from(tuz64, "base64url");
const uzunlik = kutilgan.length;
const hash = await scryptP(parol, tuz, uzunlik, { N: Number(n) });
return timingSafeEqual(hash, kutilgan);
}
const a = await parolniHashla("toshkent2026");
const b = await parolniHashla("toshkent2026");
console.log(a.split("$").length, a === b);
console.log(await parolniTekshir("toshkent2026", a));
console.log(await parolniTekshir("Toshkent2026", a));Konsolda:
4 false
true
falseBir xil parol — ikki xil saqlangan qator (a === b — false): tuz ishladi. To'g'ri parol — true, bitta katta harf farqi — false.
Saqlash formatiga qarang: scrypt$16384$<tuz>$<hash>. Algoritm nomi va parametrlar ham saqlanadi. Bir yildan keyin N ni oshirmoqchi bo'lsangiz, eski yozuvlar o'z parametri bilan tekshiriladi. Foydalanuvchi keyingi safar kirganda — yangi parametr bilan qayta hash qilinadi. Parolning o'zi hech qayerda yo'q.
promisify (util moduli) — scrypt callback'li, sinxron scryptSync esa event loop'ni 50–300 ms to'xtatadi. Serverda faqat asinxron variant: hash libuv thread pool'ida hisoblanadi (thread pool).
2.4 Tuzoq: maxmem
Kanon parametrini (N: 2 ** 17) to'g'ridan bersak:
import { scryptSync } from "node:crypto";
try {
scryptSync("parol", "tuz", 32, { N: 2 ** 17 });
} catch (xato) {
console.log(xato.code);
}Konsolda:
ERR_CRYPTO_INVALID_SCRYPT_PARAMSTo'liq xabar: Invalid scrypt params: error:030000AC:digital envelope routines::memory limit exceeded — "xotira chegarasidan oshdi". scrypt 128 × N × r bayt xotira oladi: N=2^17, r=8 da — 128 MiB. Node'ning sukut chegarasi (maxmem) — 32 MiB. Kanon shuning uchun maxmem: 256 * 2 ** 20 qo'shgan. Xabar sababni emas, OpenSSL ichki kodini ko'rsatadi — shuning uchun bu tuzoq ko'p vaqt oladi.
2.5 argon2 Node 24 da
Ilgari argon2 uchun npm paketi kerak edi (argon2 — 2026-10-23 holatiga 0.45.1, C++ addon). Kanon topdi: Node 24.21 da crypto.argon2 va crypto.argon2Sync o'rnatilgan, chaqirilganda ogohlantirish chiqmadi. Imzo kanon tajribasidagidek:
import { argon2Sync } from "node:crypto";
const hash = argon2Sync("argon2id", {
message: "parol123",
// tuz; haqiqiy kodda — randomBytes(16)
nonce: Buffer.from("0123456789abcdef"),
parallelism: 1,
tagLength: 32,
memory: 19456, // KiB = 19 MiB
passes: 2,
});
console.log(hash.length, hash.toString("hex").slice(0, 16));Konsolda:
32 ee0c314b6293859bBu yerda tuz qat'iy — natija har safar bir xil bo'lsin (faqat ko'rsatish uchun). Haqiqiy kodda randomBytes(16).
Bu API qanchalik ishonchli? Node hujjatiga ko'ra (2026-10 holatiga), crypto.argon2 v24.7.0 da qo'shilgan. Unga alohida "eksperimental" belgisi qo'yilmagan — demak butun node:crypto moduli kabi Stability: 2 — Stable (barqaror). Lekin u yangi: Node 22 da ham, 24.7 dan oldingi 24.x da ham yo'q. Shuning uchun package.json da engines.node ni >=24.7 qilib qo'ying yoki eski Node uchun argon2 / @node-rs/argon2 paketini ishlating.
bcrypt — eng keng tarqalgan eski variant (bcrypt 6.0.0 — addon, bcryptjs 3.0.3 — toza JS). Uning bitta tuzog'i: parolning faqat birinchi 72 baytini hisobga oladi, qolgani jim tashlanadi. Yangi loyiha uchun — argon2id.
Tekshirib ko'ring: Nega tuz sir emas va hash yonida ochiq saqlanadi? U ham yashirin bo'lsa, xavfsizroq emasmi?
Javob
Tuzning vazifasi — bir xil parollarga har xil hash berish va tayyor jadvallarni yaroqsiz qilish. Buning uchun u faqat noyob bo'lishi kerak, sir emas. Tekshirishda tuz kerak — shuning uchun hash yonida turadi. Qo'shimcha maxfiy kalit (pepper) ham qo'shish mumkin, u bazadan alohida (env'da) saqlanadi — lekin bu ixtiyoriy qatlam.
3. Shifrlash: AES-256-GCM
3.1 Uch tarkibiy qism
Hash bir tomonlama. Shifrlash (encryption) esa ikki tomonlama: kalit bilan yopiladi, o'sha kalit bilan ochiladi. Bitta kalit ikkala ishni qilgani uchun bu simmetrik shifrlash deyiladi. Bugungi standart tanlov — AES-256-GCM:
| Qism | Hajm | Vazifasi |
|---|---|---|
| Kalit | 32 bayt (256 bit) | Sir. randomBytes(32) yoki parol'dan KDF bilan |
| IV (nonce) | 12 bayt | Har shifrlashda yangi tasodif; sir emas, shifr yonida saqlanadi |
| Auth tag | 16 bayt | Shifr buzilmaganini tasdiqlaydi (GCM — shifrlash + butunlik) |
import {
createCipheriv, createDecipheriv, randomBytes,
} from "node:crypto";
const kalit = randomBytes(32);
function shifrla(matn) {
const iv = randomBytes(12);
const c = createCipheriv("aes-256-gcm", kalit, iv);
const shifr = Buffer.concat([c.update(matn, "utf8"), c.final()]);
return { iv, shifr, tag: c.getAuthTag() };
}
function och({ iv, shifr, tag }) {
const d = createDecipheriv("aes-256-gcm", kalit, iv);
d.setAuthTag(tag);
return Buffer.concat([d.update(shifr), d.final()]).toString("utf8");
}
const quti = shifrla("API kaliti: sk_test_123");
console.log(quti.shifr.length, quti.tag.length);
console.log(och(quti));
const buzuq = { ...quti, shifr: Buffer.from(quti.shifr) };
buzuq.shifr[0] ^= 1; // bitta bitni o'zgartiramiz
try {
och(buzuq);
} catch (xato) {
console.log(xato.message);
}Konsolda:
23 16
API kaliti: sk_test_123
Unsupported state or unable to authenticate dataKuzatuvlar:
- Shifr 23 bayt — asl matn bilan bir xil uzunlik. GCM to'ldirma (padding) qo'shmaydi.
- Bitta bit o'zgardi —
final()xato tashladi: "holat qo'llab-quvvatlanmaydi yoki ma'lumotni tasdiqlab bo'lmadi". Kanon tajribasi ham aynan shu xabarni oldi. Xabar tushunarsiz, lekin ma'nosi aniq: shifr buzilgan yoki kalit/tag noto'g'ri. Buzilgan ma'lumot jim "ochilib", axlat qaytarilmaydi. ^= 1— XOR bilan bitta bitni teskarisiga o'zgartirish.
Natijani saqlashda uchalasi birga yoziladi, masalan iv.tag.shifr (har biri base64url). Kalit — alohida, sir sifatida (env, sirlar menejeri).
3.2 IV qoidasi
Bir kalit bilan bitta IV ni ikki marta ishlatmang. GCM'da bu shunchaki zaiflik emas — halokat: ikki shifrni solishtirib, matnlar haqida ma'lumot va hatto tasdiqlash kalitini tiklash mumkin. Shuning uchun IV har safar randomBytes(12). Qat'iy IV — faqat dars namoyishi uchun, kodda hech qachon.
Nega aes-256-cbc emas? Eski kodlarda u ko'p uchraydi. Lekin CBC butunlikni tekshirmaydi — buzilgan shifr jim ochilib, axlat beradi yoki "padding oracle" hujumiga yo'l ochadi. GCM shifrlash va butunlikni birga beradi (AEAD). Yangi kodda — GCM (yoki chacha20-poly1305).
3.3 Paroldan kalit
Kalit 32 tasodifiy bayt bo'lishi kerak. Foydalanuvchi esa parol beradi. Paroldan kalit yasash — yana KDF: scrypt(parol, tuz, 32). Parol hash'idagi bilan bir xil g'oya, faqat natija saqlanmaydi, kalit sifatida ishlatiladi. Tuz va IV shifr yonida saqlanadi.
Tekshirib ko'ring: Dasturchi kalit sifatida
"mening-sirli-kalitim-32-belgi!!!"satrini to'g'ridan ishlatdi (32 belgi). Nima xato?
Javob
Kalit tasodifiy emas — u insonning o'ylab topgan matni, taxmin qilish ancha oson (256 bit o'rniga amalda ancha kam). Bundan tashqari, u kodda yozilgan va GitHub'ga tushadi. To'g'risi — randomBytes(32) bilan yaratib, env'da saqlash; parol kerak bo'lsa — scrypt bilan kalit hosil qilish.
4. Imzo: Ed25519
4.1 Asimmetrik kalitlar
HMAC'da ikkala tomon bitta kalitni biladi — demak ikkalasi ham imzo yasay oladi. Agar tekshiruvchilar ko'p bo'lsa (paketni yuklagan hamma), ularga kalit berish — ularga soxta imzo yasash huquqini berish. Yechim — asimmetrik kalitlar: juftlik.
- Yopiq kalit (private key) — faqat sizda; imzolaydi.
- Ochiq kalit (public key) — hammaga; faqat tekshiradi.
Ed25519 — zamonaviy, tez, xatoga chidamli imzo algoritmi (SSH kalitlari, Git commit imzosi, ko'p protokollar).
import { generateKeyPairSync, sign, verify } from "node:crypto";
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
const fayl = Buffer.from("login-valyuta-1.0.0.tgz ichidagi baytlar");
const imzo = sign(null, fayl, privateKey);
console.log(imzo.length);
console.log(verify(null, fayl, publicKey, imzo));
const boshqa = Buffer.from("login-valyuta-1.0.0.tgz o'zgartirilgan");
console.log(verify(null, boshqa, publicKey, imzo));Konsolda:
64
true
falseKanon natijasi bilan bir xil: imzo 64 bayt, to'g'ri fayl — true, boshqasi — false. sign ning birinchi argumenti — hash algoritmi; Ed25519 uchun null (u o'zi ichida hash qiladi).
Ochiq kalitni tarqatish uchun matnga aylantiriladi:
publicKey.export({ type: "spki", format: "pem" });
// -----BEGIN PUBLIC KEY-----
// MCowBQYDK2VwAyEA…
// -----END PUBLIC KEY-----4.2 Web Crypto: ECDSA P-256
Brauzerda (va Node'da) xuddi shu g'oya — crypto.subtle. Web push darsida VAPID kalitlari ECDSA P-256 juftligi ekanini ko'rgan edik. Bu blok saytda ham ishlaydi:
const juft = await crypto.subtle.generateKey(
{ name: "ECDSA", namedCurve: "P-256" },
true,
["sign", "verify"],
);
const xom = await crypto.subtle.exportKey("raw", juft.publicKey);
console.log(new Uint8Array(xom).length);
const algoritm = { name: "ECDSA", hash: "SHA-256" };
const xabar = new TextEncoder().encode("100 USD");
const { privateKey, publicKey } = juft;
const imzo = await crypto.subtle.sign(algoritm, privateKey, xabar);
const { subtle } = crypto;
const ok = await subtle.verify(algoritm, publicKey, imzo, xabar);
console.log(imzo.byteLength, ok);Konsolda:
65
64 true65 bayt — siqilmagan P-256 ochiq kalit (0x04 + 32 bayt x + 32 bayt y). Aynan shu 65 bayt base64url bilan VAPID kalit sifatida beriladi. Web Crypto'da hamma narsa asinxron va kalit obyektlari ichida — yopiq kalitni extractable: false bilan yaratsangiz, uni hatto o'z kodingiz ham eksport qilolmaydi.
5. Uch vazifa — bitta jadval
Jarayonni bir rasmda ko'ring. Har vazifa uchun nima kiradi va nima chiqadi:
flowchart LR
P["parol"] -->|"argon2id + tuz"| H["hash: ochilmaydi"]
M["ma'lumot"] -->|"AES-GCM + kalit"| S["shifr: kalit ochadi"]
F["fayl"] -->|"Ed25519 yopiq kalit"| I["imzo: ochiq kalit bilan"]| Vazifa | Vosita | Kalit | Ortga qaytadimi |
|---|---|---|---|
| Parol saqlash | argon2id, scrypt | yo'q (tuz) | Yo'q |
| Xabar butunligi (ikki tomon) | HMAC | bitta sir | Yo'q (faqat solishtirish) |
| Ma'lumotni yashirish | AES-256-GCM | bitta sir | Ha, kalit bilan |
| Muallifni isbotlash (ko'p tekshiruvchi) | Ed25519, ECDSA | juftlik | Yo'q (faqat tekshirish) |
6. Hujumchi nigohi
| Xato | Hujum | To'g'risi |
|---|---|---|
sha256(parol) |
Videokartada milliardlab urinish | argon2id / scrypt |
| Tuzsiz hash | Rainbow table, bir xil parollar ko'rinadi | Har foydalanuvchiga randomBytes(16) |
Hash'ni === bilan solishtirish |
Vaqt hujumi | timingSafeEqual |
| Qayta ishlatilgan IV | GCM'da kalit va matn sizib chiqadi | Har safar randomBytes(12) |
aes-*-cbc, auth tag'siz |
Shifrni o'zgartirish, padding oracle | GCM (AEAD) |
| Kalit kodda | GitHub'dan o'g'irlanadi | env / sirlar menejeri |
bcrypt da 72 baytdan uzun parol |
Oxiri jim tashlanadi | argon2id yoki uzunlikni cheklash |
Eng muhim qoida: o'z kriptografiyangizni o'ylab topmang. Algoritmlarni birlashtirish, "o'z" shifringizni yozish — deyarli har doim zaif. Standart vositalarni standart parametrlar bilan ishlating.
7. CLI qadami o'rniga: nega valyuta ga kirmadi
Bu darsda valyuta ga kod qo'shilmaydi. Sababi o'lchov bilan aniq: CLI'da parol yo'q, maxfiy ma'lumot yo'q, imzolanadigan narsa yo'q. Kesh — ochiq valyuta kurslari. Kanon t21-crypto2 tajribasi alohida skriptda qilindi va natijalari yuqorida (scrypt 286 ms, argon2id 31 ms, AES-GCM buzilgan shifr xatosi, Ed25519 64 bayt, timingSafeEqual uzunlik xatosi).
Qachon kerak bo'lardi? Agar valyuta pullik API kalitini saqlasa — u diskda AES-GCM bilan yoki, yaxshisi, OS kalit omborida (Windows Credential Manager, macOS Keychain) turishi kerak. Paketni imzolash esa npm'ning o'zida bor: trusted publishing'dagi provenance (npm paket nashri) — Sigstore imzosi.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
ERR_CRYPTO_INVALID_SCRYPT_PARAMS … memory limit exceeded |
N katta, maxmem sukut 32 MiB |
maxmem: 256 * 2 ** 20 |
Unsupported state or unable to authenticate data |
Shifr/tag/kalit/IV mos emas yoki buzilgan | Saqlash formatini tekshiring; buzilgan ma'lumotni rad eting |
Invalid key length |
Kalit 32 bayt emas | randomBytes(32) yoki KDF |
Invalid initialization vector |
IV hajmi noto'g'ri | GCM uchun 12 bayt |
| Server kirishda "qotib" qoladi | scryptSync/argon2Sync event loop'da |
Asinxron variant |
| Hamma parol "noto'g'ri" bo'lib qoldi | Parametr yoki tuz saqlanmagan | algoritm$parametr$tuz$hash formati |
9. Mashqlar
1-mashq (oson): qaysi vosita?
- Foydalanuvchi parolini saqlash:
- Hash'ni har foydalanuvchida noyob qiladigan tasodifiy qo'shimcha:
- AES-GCM'da har shifrlashda yangi bo'lishi shart bo'lgan qiymat:
- Imzoni tekshirish uchun kerak bo'lgan kalit:
- Ed25519 imzosi necha bayt?
Yechim
- argon2id — OWASP birinchi tavsiyasi (scrypt ham to'g'ri).
- tuz (salt) —
randomBytes(16), ochiq saqlanadi. - IV (nonce) — 12 bayt, qayta ishlatilmaydi.
- ochiq — yopiq kalit faqat imzolaydi.
- 64 — kanon o'lchovi ham shunday.
2-mashq (o'rta): shifrni matn sifatida saqlash
yopish(matn, kalit) → "iv.tag.shifr" (har qism base64url) va ochish(satr, kalit) funksiyalarini yozing. Ikki marta yopib, satrlar har xil, lekin ikkalasi ham to'g'ri ochilishini ko'rsating. Boshqa kalit bilan ochishga urinib, xatoni ushlang.
Yechim
import {
createCipheriv, createDecipheriv, randomBytes,
} from "node:crypto";
const b64 = (b) => b.toString("base64url");
function yopish(matn, kalit) {
const iv = randomBytes(12);
const c = createCipheriv("aes-256-gcm", kalit, iv);
const shifr = Buffer.concat([c.update(matn, "utf8"), c.final()]);
return [iv, c.getAuthTag(), shifr].map(b64).join(".");
}
function ochish(satr, kalit) {
const [iv, tag, shifr] = satr
.split(".")
.map((q) => Buffer.from(q, "base64url"));
const d = createDecipheriv("aes-256-gcm", kalit, iv);
d.setAuthTag(tag);
return Buffer.concat([d.update(shifr), d.final()]).toString("utf8");
}
const kalit = randomBytes(32);
const a = yopish("sk_test_123", kalit);
const b = yopish("sk_test_123", kalit);
console.log(a === b, a.split(".").length);
console.log(ochish(a, kalit), ochish(b, kalit));
try {
ochish(a, randomBytes(32));
} catch (xato) {
console.log("boshqa kalit:", xato.message);
}Konsolda:
false 3
sk_test_123 sk_test_123
boshqa kalit: Unsupported state or unable to authenticate dataHar yopishda IV yangi — satrlar har xil (false). Bu ham xavfsizlik: hujumchi bir xil ma'lumot ikki marta saqlanganini bilmaydi. Noto'g'ri kalit — tag mos kelmaydi, ochilmaydi.
3-mashq (qiyin): parametr yangilanishi
«scrypt bilan saqlash va tekshirish» bo'limidagi formatdan foydalanib kirish(parol, saqlangan, joriyN) yozing: parolni tekshirsin va to'g'ri bo'lsa-yu, saqlangan N joriyN dan kichik bo'lsa — yangi hash ham qaytarsin: { ok: true, yangi: "scrypt$…" }. Noto'g'ri parol — { ok: false }. Eski N=2^12 bilan saqlab, N=2^14 bilan kiring.
Yechim
import { randomBytes, scrypt, timingSafeEqual } from "node:crypto";
import { promisify } from "node:util";
const scryptP = promisify(scrypt);
const b64 = (b) => b.toString("base64url");
async function hashla(parol, N) {
const tuz = randomBytes(16);
const hash = await scryptP(parol, tuz, 32, { N });
return `scrypt$${N}$${b64(tuz)}$${b64(hash)}`;
}
async function kirish(parol, saqlangan, joriyN) {
const [, n, tuz64, hash64] = saqlangan.split("$");
const kutilgan = Buffer.from(hash64, "base64url");
const tuz = Buffer.from(tuz64, "base64url");
const hash = await scryptP(parol, tuz, 32, { N: Number(n) });
if (!timingSafeEqual(hash, kutilgan)) return { ok: false };
if (Number(n) >= joriyN) return { ok: true };
return { ok: true, yangi: await hashla(parol, joriyN) };
}
const eski = await hashla("toshkent2026", 2 ** 12);
const natija = await kirish("toshkent2026", eski, 2 ** 14);
console.log(natija.ok, natija.yangi.split("$")[1]);
console.log(await kirish("toshkent2025", eski, 2 ** 14));
const keyin = await kirish("toshkent2026", natija.yangi, 2 ** 14);
console.log(keyin);Konsolda:
true 16384
{ ok: false }
{ ok: true }Parolni qayta hash qilish faqat foydalanuvchi kirganda mumkin — o'sha paytdagina ochiq parol qo'lingizda. Shuning uchun parametr yangilanishi bosqichma-bosqich: kirgan foydalanuvchilar yangi formatga o'tadi. Haqiqiy auth tizimini Parolni xavfsiz saqlash darsida qurasiz.
10. Real ishda
- Auth — ro'yxatdan o'tish va kirish: argon2id/bcrypt. Express, NestJS, Next.js'dagi tayyor kutubxonalar (Auth.js, Lucia uslubidagi yechimlar) ichida shu.
- Ma'lumotlar bazasida maydon shifrlash — pasport raqami, telefon kabi shaxsiy ma'lumot AES-GCM bilan; kalitlar KMS (AWS KMS, Google Cloud KMS) da. O'zbekistonning shaxsiy ma'lumotlar qonuni (O'RQ-547) talablari bilan bog'liq (shaxsiy ma'lumotlar qonunchiligi).
- Imzolar — JWT (RS256/EdDSA), npm provenance, Git commit imzosi, Telegram/Stripe webhook'lari (HMAC).
- Intervyu savollari: "Parolni qanday saqlaysiz?", "Shifrlash va hash farqi?", "Nega IV qayta ishlatilmaydi?", "Simmetrik va asimmetrik farqi?".
Xulosa
- Parol — sekin KDF bilan: argon2id (Node 24 da
crypto.argon2, kanon: 31 ms, 19 MiB) yoki scrypt (286 ms); har foydalanuvchiga tuz, formatalgoritm$parametr$tuz$hash. - SHA-256 parol uchun emas — u tez; bcrypt 72 baytdan uzun parolni kesadi.
- AES-256-GCM: 32 bayt kalit, har safar yangi 12 bayt IV, 16 bayt auth tag — buzilgan shifr xato beradi (
Unsupported state or unable to authenticate data). - Ed25519: yopiq kalit imzolaydi, ochiq kalit tekshiradi (64 bayt); brauzerda —
crypto.subtle(ECDSAP-256, 65 baytlik ochiq kalit). valyutaga kirmadi — CLI'da parol va sir yo'q; o'z kriptografiyangizni o'ylab topmang.
Keyingi dars: zlib: siqish — gzip, brotli va zstd o'lchovlari va nega valyuta keshini siqmaymiz.
Manbalar
- Node.js 24 hujjati — Crypto (
scrypt,argon2— "added: v24.7.0", alohida stability belgisisiz, modul — Stability 2;createCipheriv,generateKeyPair,sign,verify) - OWASP — Password Storage Cheat Sheet (argon2id m=19 MiB t=2 p=1; pbkdf2 600 000)
- RFC 9106 — Argon2; RFC 7914 — scrypt; RFC 8032 — Ed25519; NIST SP 800-38D — GCM
- MDN — SubtleCrypto
- npm: argon2 0.45.1, bcrypt 6.0.0, bcryptjs 3.0.3 — 2026-10-23 holatiga
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!