crypto — hash, hmac, randomBytes
Muammodan boshlaylik
Foydalanuvchi parolini ma'lumotlar bazasiga saqlashingiz kerak. Qanday saqlaysiz?
// Xato — hech qachon bunday qilmang:
db.save({ username: 'ali', password: 'qwerty123' });
Ma'lumotlar bazasi buzilsa — hamma parollar ochiq ko'rinadi. Millionlab foydalanuvchi zarar ko'radi.
To'g'ri usul — parolni hash qilib saqlash. Hash — bir tomonlama funksiya. Paroldan hash hosil qilish oson, lekin hashdan parolni tiklash — amalda imkonsiz.
'qwerty123' → hash → '$2b$10$...' ← saqlanadi
'$2b$10$...' → ??? → 'qwerty123' ← imkonsiz
crypto moduli aynan shu va boshqa kriptografik operatsiyalar uchun.
crypto moduli nima?
crypto — Node.js ning o'rnatilgan kriptografiya moduli. Hash, HMAC, shifrlash, tasodifiy sonlar — bularning hammasi shu yerda. Alohida o'rnatish shart emas.
const crypto = require('crypto');
Hash nima va qanday ishlaydi?
Hash — ixtiyoriy uzunlikdagi ma'lumotni qat'iy uzunlikdagi belgilar ketma-ketligiga aylantiruvchi funksiya.
'salom' → SHA256 → 'f4d8b2a1...' (64 belgi)
'salom!' → SHA256 → '9c3e7f2b...' (64 belgi) ← bitta ! farq, natija butunlay boshqa
'Bu juda uzun...' → SHA256 → 'a1b2c3d4...' (64 belgi) ← har doim 64 belgi
Hash ning xususiyatlari:
- Bir tomonlama — hashdan asl ma'lumotni tiklash imkonsiz
- Deterministik — bir xil kirish har doim bir xil hash beradi
- Avalanche effect — kichik o'zgarish butunlay boshqa hash beradi
- Qat'iy uzunlik — kirish qanchalik katta bo'lmasin, natija bir xil uzunlikda
createHash — hash hosil qilish
const crypto = require('crypto');
const hash = crypto
.createHash('sha256') // algoritm tanlash
.update('salom dunyo') // ma'lumot berish
.digest('hex'); // natijani hex formatda olish
console.log(hash);
// 'b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576...'
Algoritmlar:
md5— tez, lekin xavfsiz emas (parol uchun ishlatmang)sha1— eskirgan, xavfsiz emassha256— keng ishlatiladi, xavfsizsha512— sha256 dan kuchliroq, lekin sekinroq
Parol uchun sha256 ham yetarli emas — bu haqda pastda.
digest formatlari:
.digest('hex') // '5d41402abc4b...' ← 16-lik, eng ko'p ishlatiladi
.digest('base64') // 'XUFAKrxLKna5cZ...' ← base64, qisqaroq
.digest('binary') // xom baytlar
Fayl integrity tekshirish
Hash ning keng tarqalgan ishlatilishi — fayl o'zgarmaganligini tekshirish. Siz fayl yuklab oldingiz. U to'liq va buzilmagan ekanini qanday bilasiz?
const crypto = require('crypto');
const fs = require('fs');
function fileHash(filePath) {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('sha256');
const stream = fs.createReadStream(filePath);
stream.on('data', chunk => hash.update(chunk));
stream.on('end', () => resolve(hash.digest('hex')));
stream.on('error', err => reject(err));
});
}
const hash = await fileHash('setup.exe');
console.log(hash);
// '3b4c5d...' ← bu qiymatni sayt da ko'rsatilgan bilan solishtirish mumkin
Stream bilan ishlash muhim — katta faylni xotiraga yuklamasdan hash hisoblanadi.
Parol uchun hash — nima uchun sha256 yetarli emas?
Sha256 tez ishlaydi — bu uning kamchiligi. Hacker millionlab parolni sinab ko'rishi mumkin (brute force). Shuning uchun parol uchun maxsus algoritmlar ishlatiladi: bcrypt, argon2, scrypt.
Node.js da scrypt o'rnatilgan:
const crypto = require('crypto');
const util = require('util');
const scrypt = util.promisify(crypto.scrypt);
async function hashPassword(password) {
const salt = crypto.randomBytes(16).toString('hex'); // tasodifiy tuz
const hash = await scrypt(password, salt, 64); // 64 bayt hash
return `${salt}:${hash.toString('hex')}`; // salt va hash birga saqlanadi
}
async function verifyPassword(password, stored) {
const [salt, hash] = stored.split(':');
const newHash = await scrypt(password, salt, 64);
return crypto.timingSafeEqual(
Buffer.from(hash, 'hex'),
newHash
);
}
// Ishlatish:
const stored = await hashPassword('qwerty123');
console.log(stored);
// '3f8a2b...:9c4e7d...'
const isValid = await verifyPassword('qwerty123', stored);
console.log(isValid); // true
const isWrong = await verifyPassword('noto\'g\'ri', stored);
console.log(isWrong); // false
Salt nima? — Tasodifiy qo'shimcha. Ikki foydalanuvchi bir xil parol ishlatsa ham, saltlar har xil bo'lgani uchun hashlari ham har xil bo'ladi. Bu "rainbow table" hujumlaridan himoya qiladi.
timingSafeEqual nima uchun? — Oddiy === taqqoslash vaqt farqiga qarab hujumga ochiq (timing attack). timingSafeEqual har doim bir xil vaqt sarflaydi.
HMAC — xabar autentifikatsiyasi
HMAC (Hash-based Message Authentication Code) — hash va maxfiy kalit kombinatsiyasi. Xabar o'zgarmaganligini va uni kim yuborganini tekshirish uchun.
Oddiy hash bilan farqi: hash faqat ma'lumot o'zgarmaganligini tekshiradi. HMAC esa kalit ham kerak — kalitsiz HMAC ni soxtalashtirish imkonsiz.
const hmac = crypto
.createHmac('sha256', 'maxfiy-kalit')
.update('Ali 1000 so\'m yubordi')
.digest('hex');
console.log(hmac);
// 'a3f8b2...'
Amaliy ishlatilishi — API so'rovlarini tekshirish:
// Server tomonida:
function signPayload(data, secret) {
return crypto
.createHmac('sha256', secret)
.update(JSON.stringify(data))
.digest('hex');
}
function verifyPayload(data, signature, secret) {
const expected = signPayload(data, secret);
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
}
const SECRET = process.env.WEBHOOK_SECRET;
const payload = { userId: 1, amount: 1000 };
const sig = signPayload(payload, SECRET);
// Webhook kelganda tekshirish:
const isValid = verifyPayload(payload, sig, SECRET);
randomBytes — xavfsiz tasodifiy sonlar
Math.random() kriptografik jihatdan xavfsiz emas — uning natijasini taxmin qilish mumkin. Tokenlar, parollar, session ID lar uchun crypto.randomBytes ishlatiladi.
// Tasodifiy baytlar:
const bytes = crypto.randomBytes(32);
console.log(bytes.toString('hex'));
// 'a3f8b2c1...' ← 64 belgi (32 bayt × 2)
// Tasodifiy token (URL xavfsiz):
const token = crypto.randomBytes(32).toString('base64url');
console.log(token);
// 'A3f8B2c1...' ← 43 belgi
Amaliy ishlatilishi — email tasdiqlash tokeni:
async function createVerificationToken(userId) {
const token = crypto.randomBytes(32).toString('hex');
const expires = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 soat
await db.save({ userId, token, expires });
return token;
}
// Foydalanuvchiga email yuboriladi:
// https://example.com/verify?token=a3f8b2c1...
UUID yaratish
Node.js 14.17+ da crypto.randomUUID() bor:
const id = crypto.randomUUID();
console.log(id);
// '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
Tez-tez uchraydigan xatolar
Parolni sha256 bilan hash qilish:
// Xato — brute force ga ochiq:
const hash = crypto.createHash('sha256').update(password).digest('hex');
Sha256 juda tez — hacker sekundiga milliardlab urinish qila oladi. Parol uchun scrypt, bcrypt yoki argon2 ishlatiladi.
Salt ishlatmaslik:
// Xato — ikki foydalanuvchi bir xil parol ishlatsa, hashlari ham bir xil:
const hash = crypto.createHash('sha256').update(password).digest('hex');
// 'qwerty123' → har doim bir xil hash
// Hacker bir hashni cracklab, boshqalarini ham topadi
=== bilan hash taqqoslash:
// Xato — timing attack ga ochiq:
if (hash1 === hash2) { ... }
// To'g'ri:
if (crypto.timingSafeEqual(Buffer.from(hash1), Buffer.from(hash2))) { ... }
Xulosa
crypto — Node.js ning kriptografiya moduli. Asosiy uchta narsa:
Hash — ma'lumotni bir tomonlama o'zgartirish. Fayl integrity, ma'lumot tekshirish uchun. Parol uchun emas.
HMAC — kalit bilan hash. API autentifikatsiya, webhook tekshirish uchun.
randomBytes — kriptografik xavfsiz tasodifiy sonlar. Token, session ID, salt yaratish uchun.
Parol uchun — scrypt (o'rnatilgan) yoki bcrypt (npm paketi).
Keyingi dars
16-dars: EventEmitter — on, emit, once, off, custom event