IlmHamroh
JavaScript Full-stack/24-qism. Node.js20/55-dars17 daqiqa
Mundarija (28)

Node.js crypto: xavfsiz tasodif, SHA-256 hash va HMAC

Qisqacha: Token, parol tiklash kodi, sessiya ID — faqat kriptografik tasodif bilan: crypto.randomBytes, crypto.randomUUID, crypto.randomInt. Math.random taxmin qilinadigan ketma-ketlik beradi — xavfsizlik uchun yaramaydi. Hash (SHA-256) — istalgan ma'lumotdan 32 baytlik "barmoq izi": bir xil kirish — bir xil hash, bitta harf farqi — butunlay boshqa hash, orqaga qaytarib bo'lmaydi. HMAC — hash + maxfiy kalit: "bu xabarni kalit egasi yuborgan va u o'zgarmagan" degan imzo. Imzolarni solishtirish — faqat timingSafeEqual bilan.

Bu darsda

  • Math.random nega xavfli ekanini tushuntirib, to'g'ri tasodif funksiyasini tanlay olasiz.
  • SHA-256 hash'ni Node (createHash) va brauzerda (crypto.subtle) hisoblaysiz; hex, base64, base64url farqini bilasiz.
  • HMAC bilan xabar imzolaysiz va imzoni vaqt hujumidan himoyalangan holda tekshirasiz.
  • valyuta keshiga URL hash'idan kalit berasiz va holat 20 ni commit qilasiz.

Oldin bilishingiz kerak: util moduli, Xavfsiz fayl operatsiyalari, Math obyekti, Route Handlers.

1. Nega bu kerak?

valyuta keshida hozir bitta fayl bor — kurslar.json. O'tgan darsda esa --sana bayrog'i qo'shildi. Endi valyuta kurs USD --sana 2026-10-01 va valyuta kurs USD turli javob beradi. Ikkalasi bitta faylga yozilsa, bugungi so'rov 1-oktyabr kursini ko'rsatib yuboradi. Har so'rovga o'z fayli kerak.

Fayl nomini so'rov URL'idan yasash mumkin, lekin URL'da :, /, ? bor — fayl nomida ular taqiqlangan. URL'ni qisqa, xavfsiz, har safar bir xil nomga aylantiradigan narsa kerak. Bu — hash.

Ikkinchi mavzu — tasodif. Atomik yozish vaqtinchalik fayl nomi uchun randomBytes ishlatdi. Math obyekti darsida esa "xavfsiz tasodif uchun crypto bor" degan edik. Bugun nega ekanini ko'ramiz. Ertaga siz server yozasiz va u parol tiklash havolalari, sessiyalar, API kalitlarini yaratadi. Ular taxmin qilinsa — hisob o'g'irlanadi.

O'xshatish: muhr va barmoq izi. Hash — barmoq izi: odamni (ma'lumotni) aniq tanitadi, lekin izdan odamni "qaytarib yasab" bo'lmaydi. HMAC — notarius muhri: faqat muhr egasi bosa oladi, va hujjatning bitta harfi o'zgarsa, muhr mos kelmay qoladi.

2. Tasodif: Math.random va crypto

2.1 Math.random nega xavfli

Math.random — psevdotasodif (pseudo-random) generator. U kichik ichki holatdan matematik formula bilan keyingi sonni hisoblaydi. V8 bunda xorshift128+ algoritmini ishlatadi (V8 blogi, 2015). Ichki holat bor-yo'g'i 128 bit. Tadqiqotchilar bir nechta ketma-ket chiqishdan holatni tiklab, keyingi sonlarni oldindan aytish mumkinligini ko'rsatgan.

Ya'ni Math.random().toString(36).slice(2) bilan parol tiklash kodi yasasangiz, hujumchi o'zi uchun bir nechta kod so'rab, sizning kodingizni hisoblab chiqishi mumkin. Shuning uchun qoida:

Vazifa Nima ishlatiladi
O'yin, animatsiya, test ma'lumoti, aralashtirish Math.random — yetarli
Token, sessiya ID, parol tiklash kodi, kalit, tuz (salt) crypto — majburiy

2.2 Kriptografik tasodif

CSPRNG (cryptographically secure pseudo-random number generator) — OS'ning xavfsiz tasodif manbai ustida ishlaydigan generator. Uning chiqishidan keyingisini taxmin qilib bo'lmaydi. Node'da uchta asosiy funksiya:

js
import { randomBytes, randomInt, randomUUID } from "node:crypto";

const token = randomBytes(32).toString("base64url");
console.log(token.length);
console.log(randomUUID().length);
const kod = randomInt(100000, 1000000); // 6 xonali SMS kod
console.log(kod >= 100000 && kod < 1000000);
console.log(/^[0-9a-f-]{36}$/.test(randomUUID()));

Konsolda:

text
43
36
true
true

Natija qiymatlari har safar boshqa — shuning uchun faqat ularning shaklini chiqardik.

Funksiya Nima qaytaradi Qachon
randomBytes(n) n tasodifiy bayt (Buffer) Token, kalit, tuz
randomUUID() "3f2a…-…" — 36 belgilik UUID v4 Yozuv ID'si
randomInt(min, max) min ≤ son < max SMS/email kod, tasodifiy tanlov

32 bayt = 256 bit tasodif: sessiya tokeni uchun odatiy hajm. base64url dan keyin 43 belgi chiqadi.

randomInt ni o'zingiz randomBytes dan yasamang. bayt % 1000000 kabi kod noteng taqsimot beradi — ba'zi sonlar boshqalaridan tez-tez chiqadi (modulo bias). randomInt buni to'g'ri hal qiladi.

2.3 Brauzerda ham bor

Web Crypto — brauzer va Node'da umumiy standart. Bu blok saytda ham ishlaydi:

js
const id = crypto.randomUUID();
const baytlar = crypto.getRandomValues(new Uint8Array(16));
console.log(id.length, id[14]);
console.log(baytlar.length);

Konsolda:

text
36 4
16

id[14] — doim 4: UUID'ning shu joyi versiyani bildiradi (v4 — "tasodifiy UUID"). getRandomValues Math obyekti darsida va'da qilingan brauzer usuli; Node 24 da u global crypto da ham bor.

Tekshirib ko'ring: Telegram bot foydalanuvchiga 6 xonali tasdiq kodi yuboradi. Dasturchi Math.floor(Math.random() * 900000) + 100000 yozdi. Bu qayerda xato?

Javob

Kod xavfsizlik uchun ishlatiladi (hisobni tasdiqlash), lekin Math.random taxmin qilinadigan. To'g'risi — crypto.randomInt(100000, 1000000). Qo'shimcha himoya: urinishlar sonini cheklash va kod muddatini qisqa qilish — 6 xonali kodni million marta sinab ko'rish kerak, cheklovsiz esa bu mumkin.

3. Hash: ma'lumotning barmoq izi

3.1 SHA-256

Hash funksiya — istalgan uzunlikdagi ma'lumotni belgilangan uzunlikdagi qisqa qiymatga aylantiradi. Kriptografik hash'ning uch xususiyati bor:

  1. Deterministik — bir xil kirish, har doim bir xil natija.
  2. Ko'chki effekti (avalanche) — bitta harf farqi, butunlay boshqa natija.
  3. Bir tomonlama — hash'dan asl ma'lumotni topib bo'lmaydi; bir xil hash beradigan ikkinchi kirishni topish ham amalda imkonsiz.

Node'da createHash:

js
import { createHash } from "node:crypto";

const sha256 = (matn) =>
  createHash("sha256").update(matn).digest("hex");
console.log(sha256("USD"));
console.log(sha256("USE"));
console.log(sha256("USD") === sha256("USD"));

Konsolda:

text
a26cdf3a6e709124385d4d7eb9bff6b897a58ed5597fbab779b89849dbe81b21
b3c2ed23b44e753d6bfc54b1d48ff7d0e4959624ecf1ceb1d0942bac08548634
true

USD va USE — bitta harf farq. Hash'lari esa umuman o'xshamaydi. Uchala xususiyat ham ko'rindi.

Zanjir: createHash(algoritm) → update(ma'lumot) (bir necha marta chaqirish mumkin — katta faylni bo'laklab) → digest(format). digest dan keyin obyektni qayta ishlatib bo'lmaydi.

Algoritm Hajm Holat
sha256 32 bayt Standart tanlov
sha512 64 bayt Kuchliroq, 64-bit protsessorda tez
sha1 20 bayt Buzilgan (2017, SHAttered) — faqat eski tizimlar bilan moslik uchun
md5 16 bayt Buzilgan — xavfsizlik uchun emas, faqat checksum

Diqqat: Parolni SHA-256 bilan saqlamang. SHA-256 juda tez — videokarta sekundiga milliardlab parolni sinab ko'radi. Parol uchun ataylab sekin funksiyalar kerak (scrypt, argon2) — ular keyingi darsda.

3.2 Format: hex, base64, base64url

Hash — 32 bayt. Matnga aylantirishning uch usuli:

js
import { createHash } from "node:crypto";

const h = createHash("sha256").update("abc").digest();
console.log(h.length);
console.log(h.toString("hex"));
console.log(h.toString("base64"));
console.log(h.toString("base64url"));

Konsolda:

text
32
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=
ungWv48Bz-pBQUDeXa4iI7ADYaOWF3qctBD_YfIAFa0
Format Uzunlik (32 bayt) Xususiyati
hex 64 Faqat 0-9a-f, o'qish va solishtirish oson
base64 44 Qisqaroq, lekin +, /, = bor
base64url 43 + → -, / → _, = yo'q — URL va fayl nomida xavfsiz

ba7816bf… — "abc" ning SHA-256 hash'i standartdagi (FIPS 180) rasmiy test qiymati. Har qanday to'g'ri SHA-256 aynan shuni beradi — o'z kodingizni tekshirish uchun qulay. Tokenlar odatda base64url da (JWT ham shunday), fayl nomlari va loglarda — hex.

3.3 Brauzerda: crypto.subtle.digest

Web Crypto'da hash — asinxron va ArrayBuffer qaytaradi. Bu blok saytda ham ishlaydi:

js
async function sha256Hex(matn) {
  const baytlar = new TextEncoder().encode(matn);
  const hash = await crypto.subtle.digest("SHA-256", baytlar);
  return [...new Uint8Array(hash)]
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

console.log(await sha256Hex("abc"));

Konsolda:

text
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

Natija createHash bilan bir xil — algoritm bitta. Node'ga xos kodni yozganda createHash qulayroq (sinxron, to'g'ridan hex). Brauzer va Node'da bir xil ishlashi kerak bo'lgan kod uchun — crypto.subtle.

Tekshirib ko'ring: Ikki odamning parol hash'i bir xil chiqdi. Bu nimani anglatadi?

Javob

Deyarli aniq — ikkalasining paroli bir xil. Hash deterministik. Bu ham parolni oddiy hash bilan saqlash xavfining bir sababi: bir xil parollar ko'rinib qoladi. Shuning uchun parolga har foydalanuvchi uchun alohida tasodifiy tuz (salt) qo'shiladi — keyingi darsda.

4. HMAC: kalit bilan imzo

4.1 Nega oddiy hash yetmaydi

Server mijozga "100 USD" degan xabar va uning SHA-256 hash'ini yubordi. Yo'lda hujumchi xabarni "900 USD" ga o'zgartirdi. Hash'ni ham o'zi qayta hisoblab qo'ya oladi — SHA-256 hamma uchun ochiq. Oddiy hash tasodifiy buzilishni ushlaydi, ataylab o'zgartirishni emas.

HMAC (hash-based message authentication code) — hash'ga maxfiy kalit qo'shadi. Kalitni bilmagan odam to'g'ri HMAC yasay olmaydi. Siz HMAC'ni Telegram Mini App va webhook imzosi darslarida ishlatgansiz — bugun uni ichidan ko'ramiz.

js
import { createHmac } from "node:crypto";

const KALIT = "sir-kalit"; // haqiqatda: env'dan, randomBytes(32)
const imzola = (xabar) =>
  createHmac("sha256", KALIT).update(xabar).digest("hex");

const imzo = imzola("100 USD");
console.log(imzo);
console.log(imzola("100 USD") === imzo);
console.log(imzola("900 USD") === imzo);

Konsolda:

text
6ad434ff681b68f111beca0c20aea87d7bfade98544859ffe661d244625971ec
true
false

Qabul qiluvchi xabarni o'zi imzolaydi va kelgan imzo bilan solishtiradi. Mos kelsa — xabar kalit egasidan va o'zgarmagan. HMAC shifrlash emas: "100 USD" matni ochiq qoladi, faqat uning haqiqiyligi tasdiqlanadi.

Ikki tomon nima qilishini rasmda ko'ring. Kalit faqat ikkala tomonda, yo'lda faqat xabar va imzo:

sequenceDiagram
  participant Y as Yuboruvchi (kalit bor)
  participant H as Yo'l (hujumchi ko'radi)
  participant Q as Qabul qiluvchi (kalit bor)
  Y->>Y: imzo = HMAC(kalit, xabar)
  Y->>H: xabar + imzo
  H->>Q: xabar + imzo
  Q->>Q: HMAC(kalit, xabar) ni qayta hisoblash
  Q->>Q: timingSafeEqual: teng — qabul, yo'q — rad

4.2 Telegram Mini App: haqiqiy misol

Telegram Mini Apps darsida initData ni serverda tekshirish kerakligini aytgan edik. Telegram hujjatidagi qoida: kalit = HMAC-SHA256("WebAppData", bot tokeni), imzo = HMAC-SHA256(kalit, maydonlar alifbo tartibida, \n bilan). Soxta token bilan:

js
import { createHmac } from "node:crypto";

const BOT_TOKEN = "123456:SOXTA-TOKEN"; // haqiqiysi — env'da
const maydonlar = { auth_date: "1761195600", user: '{"id":42}' };

const satr = Object.keys(maydonlar)
  .sort()
  .map((k) => `${k}=${maydonlar[k]}`)
  .join("\n");
const kalit = createHmac("sha256", "WebAppData")
  .update(BOT_TOKEN)
  .digest();
const hash = createHmac("sha256", kalit).update(satr).digest("hex");
console.log(JSON.stringify(satr));
console.log(hash.length);

Konsolda:

text
"auth_date=1761195600\nuser={\"id\":42}"
64

Mijoz initData da hash yuboradi, server shu hisobni qilib solishtiradi. Bot tokeni faqat serverda — brauzer uni hech qachon ko'rmaydi, demak imzoni soxtalashtira olmaydi. auth_date ni ham tekshirish kerak (masalan 24 soatdan eski — rad), aks holda eski initData qayta ishlatiladi.

4.3 timingSafeEqual: vaqt hujumi

Imzoni === bilan solishtirish xavfli. === satrlarni chapdan o'ngga solishtiradi va birinchi farqda to'xtaydi. Birinchi belgisi noto'g'ri imzo — bir zumda rad; o'nta belgisi to'g'ri — biroz kechroq. Hujumchi javob vaqtini juda ko'p marta o'lchab, imzoni belgima-belgi topishi mumkin. Bu — vaqt hujumi (timing attack).

timingSafeEqual har doim hamma baytni solishtiradi — vaqt farq qilmaydi:

js
import { createHmac, timingSafeEqual } from "node:crypto";

const KALIT = "sir-kalit";
function imzoTogrimi(xabar, kelganHex) {
  const kutilgan = createHmac("sha256", KALIT).update(xabar).digest();
  const kelgan = Buffer.from(kelganHex, "hex");
  if (kelgan.length !== kutilgan.length) return false;
  return timingSafeEqual(kelgan, kutilgan);
}

const imzo = createHmac("sha256", KALIT)
  .update("100 USD")
  .digest("hex");
console.log(imzoTogrimi("100 USD", imzo));
console.log(imzoTogrimi("900 USD", imzo));
console.log(imzoTogrimi("100 USD", "abc"));

Konsolda:

text
true
false
false

Uzunlik tekshiruvi shart. Usiz timingSafeEqual xato tashlaydi — kanon tajribasi va revalidatsiya darsida ko'rgan xatoingiz:

text
ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH Input buffers must have the same byte length

"Kirish buferlari bir xil bayt uzunligida bo'lishi kerak". Uzunlik sir emas (HMAC-SHA256 doim 32 bayt), shuning uchun uni oddiy !== bilan tekshirish xavfsiz.

Tekshirib ko'ring: Nega imzoTogrimi kelgan imzoni hex'dan Buffer'ga aylantiradi, kutilgan ni esa hex satrga aylantirmaydi?

Javob

timingSafeEqual satr emas, Buffer (yoki TypedArray) qabul qiladi. Ikkalasini bir xil ko'rinishga keltirish kerak edi — baytlarga keltirish tabiiyroq, chunki digest() formatsiz chaqirilsa, Buffer beradi. Noto'g'ri hex ("abc") Buffer.from da 1 baytga aylanadi — uzunlik tekshiruvida rad etiladi.

5. Hujumchi nigohi

Hujum Zaif kod Himoya
Tokenni taxmin qilish Math.random() bilan token randomBytes(32)
SMS kodni sinab chiqish 6 xona, cheklovsiz urinish randomInt + urinish chegarasi + qisqa muddat
Xabarni o'zgartirish Oddiy hash bilan "imzo" HMAC maxfiy kalit bilan
Imzoni vaqt bo'yicha topish imzo === kelgan timingSafeEqual + uzunlik tekshiruvi
Eski imzoni qayta yuborish (replay) Faqat imzo tekshiriladi Vaqt belgisi (auth_date) va muddat
Parol bazasi o'g'irlandi sha256(parol) scrypt/argon2 + tuz (crypto 2)
Kalit kod ichida const KALIT = "…" GitHub'da env + sirlarni boshqarish

valyuta da sir yo'q — kesh kaliti xavfsizlik uchun emas, nom uchun. Lekin xuddi shu funksiyalar keyingi qismlardagi serverda auth, webhook va to'lovlarning asosi bo'ladi.

6. CLI qadami: kesh kaliti va holat 20 commit'i

6.1 kalit.ts

Holat 20 ning ikkinchi qismi. Kanon src/kalit.ts — bitta funksiya:

ts
// Kesh kaliti: so'rov URL'ining SHA-256 hash'i (16 belgi).
// API manzili yoki sana o'zgarsa — boshqa fayl, keshlar aralashmaydi
import { createHash } from "node:crypto";

export function keshKaliti(url: string): string {
  return createHash("sha256").update(url).digest("hex").slice(0, 16);
}

(Izoh qatorini telefon uchun ikkiga bo'ldik va kanondagi "xeshi" so'zini kurs atamasi — "hash'i" — bilan yozdik; kod o'zgarmagan.) Kesh fayli endi kurslar-<kalit>.json. Kalit URL'dan olingani uchun VALYUTA_API boshqa manzilga qaratilsa ham (masalan testdagi soxta server) keshlar aralashmaydi. Kanonning haqiqiy kalitlarini qayta hisobladik:

js
import { createHash } from "node:crypto";

const keshKaliti = (url) =>
  createHash("sha256").update(url).digest("hex").slice(0, 16);

const asos = "https://cbu.uz/uz/arkhiv-kursov-valyut/json/";
console.log(keshKaliti(asos));
console.log(keshKaliti(`${asos}all/2026-10-18/`));
console.log(keshKaliti(`${asos}all/2026-10-01/`));

Konsolda:

text
7d6ac3a5dea5f15a
7d1a2c0f8ef6fbf1
b8c23ce26aa8eef7

Kanon jonli chiqishida (holat 20, haqiqiy CBU) xuddi shu nomlar:

text
$ valyuta kesh
kurslar-7d1a2c0f8ef6fbf1.json  25461 bayt  2026-10-23T05:23:05.463Z
kurslar-7d6ac3a5dea5f15a.json  25533 bayt  2026-10-23T05:23:05.976Z
kurslar-b8c23ce26aa8eef7.json  25497 bayt  2026-10-23T05:23:06.420Z
kurslar.json  25533 bayt  2026-10-23T05:19:17.109Z

Nega 16 belgi (64 bit), to'liq 64 emas? Fayl nomi qisqa bo'lsin. Bir foydalanuvchining keshida bir necha yuz fayl bo'ladi — 64 bitli hash'da tasodifiy to'qnashuv ehtimoli amalda nol (hash to'qnashuvi). Xavfsizlik kerak emas — hujumchi o'z keshini buzib, o'zidan boshqa hech kimga zarar qilolmaydi.

Ikki muddat: sanali kesh muddatsiz (o'tgan kunning kursi o'zgarmaydi), oxirgi kurs — 1 soat (fs chuqur).

6.2 Topilma: yetim kesh fayli

Oxirgi qatorga qarang: kurslar.json. Bu holat 18 formatidagi fayl — yangi kod uni endi o'qimaydi. Kesh formati o'zgarganda eski fayllar diskda yetim bo'lib qoladi. Kanon buni yashirmadi: valyuta kesh tozala ularni ham o'chiradi (glob * bilan). Saboq: kesh formatini o'zgartirsangiz, eski fayllar haqida o'ylang — avtomatik tozalash yoki versiya raqamli papka (kesh/v2/).

6.3 Holat 20 tayyor

util darsidagi parseArgs, styleText, debuglog va bugungi kalit bilan birga holat 20 ga yana ikki fayl kiradi: src/format.ts (jadval, JSON, CSV — RFC 4180 qochirish) va src/sana.ts (sananiTekshir — CBU'ning jim "oxirgi kurs" xulqiga qarshi). Kanon jonli natijasi:

text
$ valyuta kurs USD EUR --sana 2026-10-18
Eslatma: 2026-10-18 uchun kurs yo'q (dam olish kuni?) — CBU 2026-10-17 kursini berdi
2026-10-17 holatiga (api)
USD    1    12 055.90    44.25  AQSH dollari
EUR    1    13 572.53   109.87  EVRO
text
$ valyuta kurs USD --sana 2026-10-25
Xato: Kelajak sana: 2026-10-25 (bugun 2026-10-23)
[exit 2]

Kelajak sana uchun CBU API jim oxirgi kursni qaytaradi (200 bilan) — kanon buni o'lchab topdi. Shuning uchun valyuta sanani o'zi tekshiradi va javobdagi haqiqiy sanani ko'rsatadi.

Tekshiruv (tekshiruv-20): tip va lint 0, test 27/27 (+9), pack 38 fayl, 18.4 kB, sovuq start --help 76 ms, prod bog'liqlik 0.

bash
git add -A
git commit -m "Bayroqlar parseArgs bilan: --sana, --format, --version" \
  -m "Format: jadval/json/csv; rangli farq; kesh kaliti SHA-256"

Kanon xabari bitta qator — Bayroqlar parseArgs bilan: --sana, --format (jadval/json/csv), --version; rangli farq, kesh kaliti SHA-256. U 72 belgidan uzun, shuning uchun sarlavha va tanaga bo'ldik (commit xabarlari).

7. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Token taxmin qilindi Math.random randomBytes, randomUUID, randomInt
Digest already called digest() dan keyin update Har hash uchun yangi createHash
Hash har kompyuterda boshqa Satr encoding'i yoki \r\n farqi Kirishni normallashtiring, UTF-8
URL'da token buzildi base64 dagi +, / base64url
ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH Uzunliklar teng emas Avval uzunlikni tekshiring
Imzo === bilan solishtirilgan Vaqt hujumiga ochiq timingSafeEqual
Parol sha256 da Tez hash — oson buziladi scrypt/argon2 (crypto 2)

8. Mashqlar

1-mashq (oson): to'g'ri tanlov

  • Sessiya tokeni uchun funksiya: crypto.random
  • SHA-256 hash necha bayt?
  • URL uchun xavfsiz format:
  • Kalitli imzo nomi:
  • Imzoni solishtirish funksiyasi:
Yechim
  • Bytes — randomBytes(32).
  • 32 — 256 bit; hex'da 64 belgi.
  • base64url — + / = yo'q.
  • HMAC — hash + maxfiy kalit.
  • timingSafeEqual — vaqt bo'yicha teng.

2-mashq (o'rta): fayl butunligi

Fayl yozing, uning SHA-256 hash'ini hisoblang, keyin bitta belgini o'zgartirib qayta hisoblang. Funksiya faylHash(fayl) faylni readFile bilan o'qib, hex hash qaytarsin. Hash'larning birinchi 16 belgisini va tengligini chiqaring.

Yechim
js
import { createHash } from "node:crypto";
import { readFile, writeFile } from "node:fs/promises";

async function faylHash(fayl) {
  const baytlar = await readFile(fayl);
  return createHash("sha256").update(baytlar).digest("hex");
}

await writeFile("kurslar.csv", "USD,11903.15\n");
const birinchi = await faylHash("kurslar.csv");
await writeFile("kurslar.csv", "USD,11903.16\n");
const ikkinchi = await faylHash("kurslar.csv");
console.log(birinchi.slice(0, 16));
console.log(ikkinchi.slice(0, 16));
console.log(birinchi === ikkinchi);

Konsolda:

text
ffea5edaba262f84
efbad1cb96c92491
false

Bitta raqam — butunlay boshqa hash. npm integrity (npm pack) aynan shunday ishlaydi: yuklangan arxiv hash'i lock-fayldagi bilan solishtiriladi. Katta fayl uchun readFile o'rniga stream bilan bo'laklab update qilinadi (stream'lar).

3-mashq (qiyin): imzoli havola

Parol tiklash havolasi uchun ikki funksiya yozing. havolaYasa(email, muddat, kalit) → email.muddat.imzo (imzo — email.muddat ning HMAC-SHA256, base64url). havolaniTekshir(token, hozir, kalit) → email yoki xato matni: "buzilgan" (imzo mos emas), "muddati o'tgan". timingSafeEqual ishlating. email ichida nuqta bo'lishi mumkin — tokenni oxiridan bo'ling.

Yechim
js
import { createHmac, timingSafeEqual } from "node:crypto";

const imzoQil = (matn, kalit) =>
  createHmac("sha256", kalit).update(matn).digest("base64url");

function havolaYasa(email, muddat, kalit) {
  const tana = `${email}.${muddat}`;
  return `${tana}.${imzoQil(tana, kalit)}`;
}

function havolaniTekshir(token, hozir, kalit) {
  const i = token.lastIndexOf(".");
  const tana = token.slice(0, i);
  const kelgan = Buffer.from(token.slice(i + 1));
  const kutilgan = Buffer.from(imzoQil(tana, kalit));
  if (kelgan.length !== kutilgan.length) return "buzilgan";
  if (!timingSafeEqual(kelgan, kutilgan)) return "buzilgan";
  const j = tana.lastIndexOf(".");
  if (Number(tana.slice(j + 1)) < hozir) return "muddati o'tgan";
  return tana.slice(0, j);
}

const KALIT = "test-kaliti";
const t = havolaYasa("ali.valiyev@example.com", 1761200000, KALIT);
console.log(havolaniTekshir(t, 1761195600, KALIT));
console.log(havolaniTekshir(t, 1761300000, KALIT));
const soxta = t.replace("ali", "vali");
console.log(havolaniTekshir(soxta, 1761195600, KALIT));

Konsolda:

text
ali.valiyev@example.com
muddati o'tgan
buzilgan

Avval imzo, keyin muddat tekshiriladi: imzosi noto'g'ri tokendagi muddatga ishonib bo'lmaydi. Email'ni almashtirish imzoni buzdi. Bu — JWT g'oyasining soddalashtirilgan ko'rinishi; haqiqiy auth'ni keyingi qismlarda qurasiz.

4-mashq: CLI qadami

src/kalit.ts ni yozing, kesh.ts ni kurslar-<kalit>.json ga o'tkazing (oxirgi kurs — 1 soat, sanali — muddatsiz). valyuta kurs USD, valyuta kurs USD --sana 2026-10-01, valyuta kesh — ikki xil fayl ko'rinsin. npm run check toza bo'lsa — holat 20 ni commit qiling.

Yechim

Kalitlar «CLI qadami» bo'limidagi qiymatlar bilan bir xil chiqishi kerak (URL bir xil bo'lsa). Holat 18 dan qolgan kurslar.json ni valyuta kesh tozala bilan o'chiring. Testlar soni — 27.

9. Real ishda

  • Auth — sessiya tokenlari, parol tiklash havolalari, API kalitlari: randomBytes. JWT — HMAC (HS256) yoki ochiq kalitli imzo bilan.
  • Webhook'lar — Stripe, GitHub, Telegram, Click/Payme so'rovlarni HMAC bilan imzolaydi; server timingSafeEqual bilan tekshiradi.
  • Kesh va CDN — fayl nomidagi hash (app.3f2a1c.js, content hash), ETag, Git commit ID — hammasi hash.
  • Intervyu savollari: "Hash va shifrlash farqi?", "Nega Math.random token uchun yaramaydi?", "Timing attack nima?".

Xulosa

  • Xavfsizlik uchun tasodif — faqat crypto: randomBytes, randomUUID, randomInt; Math.random taxmin qilinadi.
  • SHA-256 — deterministik, ko'chki effektli, bir tomonlama 32 baytlik barmoq izi; createHash (Node) va crypto.subtle.digest (brauzer) bir xil natija beradi.
  • hex — o'qish uchun, base64url — URL va token uchun.
  • HMAC — kalitli imzo: xabar o'zgarmagan va kalit egasidan; solishtirish — timingSafeEqual + uzunlik tekshiruvi.
  • valyuta keshi endi URL hash'i bilan nomlanadi; holat 20 commit qilindi (27 test, 38 fayl).

Keyingi dars: crypto 2: parolni hash'lash, shifrlash va imzo — scrypt va argon2 o'lchovlari, AES-256-GCM va Ed25519.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Node.js crypto: xavfsiz tasodif, SHA-256 hash va HMAC — IlmHamroh