Kullanıcı parolasını DB''de plaintext veya MD5/SHA256 ile saklamak, 2026''da kabul edilemez bir hata. Modern parola hashing'' için endüstri standardı üç algoritma var: argon2id (yeni projeler için en iyi), bcrypt (köklü ve güvenli), scrypt (alternatif). Bu yazı üçünü karşılaştırıp pratik kod örnekleri verir.
Neden MD5/SHA256 Olmaz?
İlgili rehberler: SSL sertifikası nasıl alınır · OWASP Top 10 2026 · JWT güvenlik · SQL injection önleme · DDoS koruma
MD5 ve SHA256 hızlı hash fonksiyonlarıdır — saniyede milyarlarca hesap. GPU ile saniyede 100+ milyar SHA256 yapılabilir. Bir saldırgan 8 karakterli parolayı saatler içinde brute force''lar. Parola hashing fonksiyonları kasıtlı olarak yavaştır.
Üç Algoritma
| Özellik | bcrypt | argon2id | scrypt |
|---|---|---|---|
| Yıl | 1999 | 2015 | 2009 |
| Memory-hard | Kısmen | ✓ Evet | ✓ Evet |
| GPU resistant | Zayıf | Güçlü | Orta |
| ASIC resistant | Zayıf | Güçlü | İyi |
| Standard | De facto | RFC 9106 | RFC 7914 |
| OWASP önerisi | OK (yeni 2023) | Öncelikli | OK |
bcrypt
// Node.js
import bcrypt from 'bcrypt';
// Kayıt sırasında
const saltRounds = 12; // 2^12 = 4096 iteration
const hash = await bcrypt.hash(plainPassword, saltRounds);
// hash = '$2b$12$...' — self-describing
// Giriş sırasında
const isValid = await bcrypt.compare(plainPassword, hashFromDb);
// Work factor güncelleme
if (bcrypt.getRounds(hashFromDb) < 12) {
const newHash = await bcrypt.hash(plainPassword, 12);
await db.users.update({ id }, { password: newHash });
}
argon2id (Önerilen)
2015 Password Hashing Competition kazananı. 3 varyant: argon2d (GPU''ya dayanıklı), argon2i (side-channel''a dayanıklı), argon2id (her ikisinin karışımı, modern default).
// Node.js
import argon2 from 'argon2';
// Kayıt
const hash = await argon2.hash(plainPassword, {
type: argon2.argon2id,
memoryCost: 19456, // KiB (19 MiB)
timeCost: 2, // iteration
parallelism: 1
});
// hash = '$argon2id$v=19$m=19456,t=2,p=1$...'
// Giriş
const isValid = await argon2.verify(hashFromDb, plainPassword);
// Work factor güncelleme (rehash ihtiyacı)
if (argon2.needsRehash(hashFromDb, { memoryCost: 19456, timeCost: 2 })) {
const newHash = await argon2.hash(plainPassword, { ... });
await db.users.update({ id }, { password: newHash });
}
# Python
from argon2 import PasswordHasher
ph = PasswordHasher(memory_cost=19456, time_cost=2, parallelism=1)
hash = ph.hash(password)
try:
ph.verify(hash, password)
except argon2.exceptions.VerifyMismatchError:
# parola yanlış
pass
if ph.check_needs_rehash(hash):
new_hash = ph.hash(password)
scrypt
// Node.js built-in (crypto module)
import { scrypt, randomBytes } from 'crypto';
import { promisify } from 'util';
const scryptAsync = promisify(scrypt);
// Kayıt
const salt = randomBytes(16).toString('hex');
const derivedKey = await scryptAsync(plainPassword, salt, 64, {
N: 32768, // 2^15 — iteration
r: 8,
p: 1
});
const hash = `${salt}:${derivedKey.toString('hex')}`;
// Giriş
const [salt, key] = hashFromDb.split(':');
const derivedKey = await scryptAsync(plainPassword, salt, 64, { N: 32768, r: 8, p: 1 });
const isValid = timingSafeEqual(Buffer.from(key, 'hex'), derivedKey);
Salt, Pepper, Timing
- Salt her parola için unique, hash''in parçası olarak saklanır — rainbow table saldırısını engeller. Hem bcrypt hem argon2 otomatik salt üretir
- Pepper uygulamaya özel server-side secret — DB sızsa bile saldırgan parola crack edemez. Çok basit: hash = argon2(password + pepper). Pepper env var''da
- Timing-safe compare string karşılaştırması timing attack''a açık —
crypto.timingSafeEqualkullan - Generic error message giriş başarısızlığında "kullanıcı yok" vs "parola yanlış" ayırt etme — saldırgan email enumeration yapar
Work Factor Nasıl Seçilir?
Ana kural: giriş cevabı kullanıcı için tolerans gösterilebilir (~250ms). Sunucunuzda hash süresini ölçün ve bu hedefe ayarlayın. Donanım hızlandıkça work factor''u her 2 yılda bir artırın.
// Work factor kalibrasyonu
async function measure() {
const password = 'test1234test';
for (let memoryCost of [4096, 8192, 19456, 46080, 65536]) {
const start = Date.now();
await argon2.hash(password, { type: argon2.argon2id, memoryCost, timeCost: 2 });
console.log(`memoryCost ${memoryCost} = ${Date.now() - start}ms`);
}
}
// 250ms civarını tercih et
OWASP 2023 Önerileri
- argon2id: 19 MiB memory, 2 iteration, 1 parallelism
- bcrypt: cost factor 10 (minimum), 12+ modern donanımda
- scrypt: N=2^17 (131072), r=8, p=1
- Tüm parolalar en az 64 byte girdi alabilmeli (truncation yok)
Kullanıcı Parola Politikası
- Minimum 12 karakter — karmaşıklık kuralları zorlamıyor
- Haveibeenpwned API ile breached parolaları reddet
- 2FA zorunlu kritik hesaplarda
- Parola değişmesi yılda 1 kez zorlama — NIST da artık önermiyor
- Password manager kullanımını teşvik et
Migration Stratejisi
Eski MD5/SHA256 hash''leri olan DB''niz varsa — toplu crack değil, lazy migration: kullanıcı login olduğunda plaintext parolayı alırsın, doğrulanırsa argon2 ile yeniden hash''lersin. Birkaç ay içinde tüm aktif kullanıcılar migrate olur.
Web Güvenliği ve Uygulama Savunması
Modern web güvenliği katmanlı savunma (defense-in-depth) ile yapılır: TLS 1.3 ve HSTS ile şifreli iletişim, WAF (Web Application Firewall) ile OWASP Top 10 saldırılarına karşı koruma, BCrypt veya Argon2id ile parola hash'leme, JWT token'larında imza doğrulama (HMAC veya RSA), CSRF token + SameSite cookie ile cross-site istek koruması ve Content Security Policy ile XSS azaltma. SQL injection önleme için prepared statement, brute force için fail2ban veya rate limiting, DDoS koruması için Cloudflare/Anti-DDoS sağlayıcı zorunludur. Güvenlik açığı taraması (Burp Suite, OWASP ZAP) ve düzenli güvenlik denetimi; üretim ortamında veri sızıntısı ve hesap ele geçirme risklerini büyük ölçüde azaltır.
Parola hashing, 2FA entegrasyonu ve breach response planı için iletişime geçin