Bölüm 3 / 11
Kimlik doğrulama ve yetkilendirme
Authentication ile authorization farkı.
17 dakikalık okuma
İki kelime sürekli birbirine karıştırılır ve İngilizce kısaltmaları bile birbirine benzer: authentication (kimlik doğrulama, AuthN) ve authorization (yetkilendirme, AuthZ). Birincisi "sen kimsin?", ikincisi "buna hakkın var mı?" sorusudur. Bu ders ikisini net ayırıyor, ardından ilkinin en kritik parçasını — parolaların nasıl saklandığını — hash fonksiyonu düzeyinde açıyor ve ikincisinin en sık ihlal edilen kuralını gösteriyor: nesne düzeyinde yetki. Sonda üretimde asıl işi yapan iki önlem var: oran sınırlama ve kullanıcı numaralandırmayı kapatmak.
01Ne öğreneceksin
- AuthN / AuthZ ayrımı ve karşılıkları: 401 ile 403 arasındaki fark, bazen neden 404 dönülür
- Kimlik doğrulama faktörleri, MFA, TOTP ve WebAuthn'un ne çözdüğü
- Parola saklama: neden hash, neden hızlı hash yanlış, tuz ve biber, bcrypt/argon2 seçimi ve maliyet parametresinin ayarlanması
- Node'a özgü tuzak: parola hash'leme libuv iş parçacığı havuzunu doldurur (12-10 bağı)
- Kullanıcı numaralandırma ve zamanlama sızıntısı; sabit zamanlı karşılaştırma
- Kaba kuvvet, kimlik bilgisi doldurma, parola serpme; oran sınırlama ve hesap kilitlemenin kendi kendine DoS'a dönüşmesi
- Yetkilendirme modelleri: RBAC, ABAC, ReBAC; ve en sık zafiyet: IDOR
02İki ayrı soru, iki ayrı katman
Kimlik doğrulama, karşındakinin iddia ettiği kişi olduğunu kanıtlamasıdır. Girdisi bir kanıttır (parola, jeton, sertifika, biyometrik), çıktısı bir kimliktir. Yetkilendirme, doğrulanmış o kimliğin belirli bir işlemi belirli bir kaynak üzerinde yapıp yapamayacağının kararıdır. Sıra değişmez: önce kimsin, sonra hakkın var mı.
İstek gelir
│
├─▶ AuthN: çerez/jeton geçerli mi? ──── hayır ──▶ 401 Unauthorized
│ │ evet → kimlik: {id: 42, roller: ["basvuru-sahibi"]}
│ ▼
└─▶ AuthZ: bu kimlik BU kaynağa BU işlemi yapabilir mi? ── hayır ──▶ 403 Forbidden
│ evet
▼
işlem çalışır
401 mi 403 mü? HTTP'nin en çok yanlış kullanılan iki kodu bunlardır.
401 Unauthorized ismine rağmen kimlik doğrulama hatasıdır: "kim olduğunu
bilmiyorum, giriş yap ya da geçerli bir kimlik sun". 403 Forbidden
yetkilendirme hatasıdır: "kim olduğunu biliyorum, ama buna hakkın yok — tekrar giriş
yapmak bir şey değiştirmez". Ayrımın pratik değeri istemci tarafındadır: 11 ve 10.
hatta yazdığın araya girici, 401'de kullanıcıyı giriş ekranına yollar, 403'te
yollamaz — 403'te giriş ekranına atmak kullanıcıyı sonsuz döngüye sokar.
Üçüncü bir seçenek daha vardır ve bilinçli bir karardır: 403 yerine 404 dönmek. "Bu kaynak var ama göremezsin" cevabı bir bilgi sızıntısıdır — saldırgan kimliği tek tek deneyerek hangi kayıtların var olduğunu haritalayabilir. Kaynağın varlığının kendisi gizliyse (özel bir depo, başka kurumun başvurusu) 404 dönmek doğrudur. Kaynağın varlığı gizli değil, yalnızca erişim kısıtlıysa 403 daha dürüst ve hata ayıklaması kolaydır.
03Kimlik doğrulama faktörleri ve MFA
Kanıtlar üç sınıfa ayrılır: bildiğin bir şey (parola, PIN), sahip olduğun bir şey (telefon, donanım anahtarı, akıllı kart), olduğun bir şey (parmak izi, yüz). Çok faktörlü doğrulama (MFA) farklı sınıflardan en az iki kanıt ister. İki parola sormak MFA değildir; ikisi de aynı sınıftandır.
Faktörlerin gücü eşit değildir ve sıralama pratikte şöyledir:
| Yöntem | Nasıl çalışır | Zayıflığı |
|---|---|---|
| SMS kodu | Tek kullanımlık kod telefona gönderilir | SIM takası, operatör yönlendirmesi, şebeke araya girmesi — en zayıf MFA, yine de hiç yoktan iyi |
| TOTP | Paylaşılan sır + zaman → 30 saniyelik kod (uygulama üretir, ağ yok) | Kimlik avıyla anlık olarak çalınabilir; sunucuda sırrın saklanması gerekir |
| Anlık bildirim onayı | Uygulamaya "sen misin?" sorulur | Onay yorgunluğu: art arda bildirimle kullanıcı sonunda onaylar |
| WebAuthn / FIDO2 | Cihazdaki özel anahtarla imza; alan adına bağlıdır | Cihaz kaybı (yedek anahtar gerekir); kurulum maliyeti |
Son satır neden en güçlüsü: WebAuthn'da imza alan adına bağlanır, yani kullanıcı sahte bir siteye kandırılsa bile tarayıcı gerçek alan adı için üretilmiş anahtarı sahte siteye vermez. Kimlik avına karşı yapısal olarak dayanıklı tek yöntemdir; TOTP'de kullanıcı kodu sahte siteye yazabilir. Bu ayrım — "kanıt hangi bağlama bağlı" — kıdemli bir cevap işaretidir.
04Parola saklama: hash, tuz, maliyet
Kural mutlaktır: parola asla geri döndürülebilir biçimde saklanmaz. Ne düz metin, ne şifreli (şifreliyse anahtarı olan çözer ve anahtar aynı sistemdedir). Saklanan şey parolanın tek yönlü özetidir. Doğrulama, gelen parolanın aynı işlemden geçirilip saklanan özetle karşılaştırılmasıdır.
Ama sıradan bir hash fonksiyonu — SHA-256, MD5 — bu iş için yanlıştır. Sebebi ilginçtir: bu fonksiyonlar hızlı olmak üzere tasarlanmıştır. Modern bir ekran kartı saniyede milyarlarca SHA-256 hesaplar; sızmış bir tablodaki parolaların büyük kısmı sözlük saldırısıyla saatler içinde bulunur. İki ek sorun daha vardır: aynı parolayı kullanan iki kullanıcının özeti aynı çıkar (kim kiminle aynı parolayı kullanıyor, tablodan görülür) ve önceden hesaplanmış gökkuşağı tabloları aramayı bedavaya indirir.
Çözüm iki parçadır:
- Tuz (salt): her kullanıcı için üretilen rastgele bir değer, parolayla birlikte hash'lenir ve özetin yanında açıkça saklanır. Gizli olması gerekmez; işi, her kullanıcının hesabını ayrı bir problem hâline getirmek ve önceden hesaplanmış tabloları geçersiz kılmaktır.
- Yavaşlık: parola hash'leme algoritmaları bilerek pahalıdır ve pahalılığı ayarlanabilir. bcrypt'te "iş faktörü", Argon2'de bellek ve yineleme parametreleri vardır. Donanım hızlandıkça parametre artırılır.
Bir üçüncüsü de vardır: biber (pepper) — tüm kullanıcılar için ortak, veritabanında değil uygulama sırlarında tutulan ek bir değer. Yalnızca veritabanı sızarsa (tuzlar ve özetler dâhil) saldırgan biberi bilmediği için sözlük saldırısı yapamaz. Bedeli, biberin döndürülmesinin zor olmasıdır.
// Node — parola kaydı ve doğrulaması (bcrypt)
import bcrypt from "bcrypt";
const MALIYET = 12; // 2^12 tur; donanım hızlandıkça artırılır
const ozet = await bcrypt.hash(parola, MALIYET); // tuz İÇERİDE üretilir ve çıktının içine gömülür
// $2b$12$Lp7q... → algoritma $ maliyet $ tuz+özet (ayrı bir tuz sütunu GEREKMEZ)
const dogruMu = await bcrypt.compare(gelenParola, ozet); // sabit zamanlı karşılaştırma içerir
// .NET — ASP.NET Core Identity zaten doğrusunu yapar
var hasher = new PasswordHasher<Kullanici>();
string ozet = hasher.HashPassword(kullanici, parola); // PBKDF2, tuz gömülü, sürüm başlıklı
var sonuc = hasher.VerifyHashedPassword(kullanici, ozet, gelenParola);
if (sonuc == PasswordVerificationResult.SuccessRehashNeeded) { /* parametreler eskimiş → yeniden hash'le */ }
Hangi algoritma? Bugünkü tercih sırası: Argon2id (bellek-zor; GPU/ASIC saldırılarına en dirençli), scrypt, bcrypt (olgun, her yerde var, 72 bayt girdi sınırı vardır — çok uzun parolalar sessizce kırpılır), PBKDF2 (yaygın ve uyumluluk gerekçesiyle hâlâ kullanılır, ama bellek-zor olmadığı için en zayıfı). SHA/MD5 tek başına hiçbir koşulda listede değildir.
Maliyet parametresi nasıl seçilir? Ölçerek. Hedef, kendi üretim donanımında
tek doğrulamanın ~100-250 ms sürmesidir. Daha düşükse saldırgana kolaylık,
daha yüksekse giriş anında kendi sunucunu yorarsın. Parametre koda gömülü sabit
değil, yapılandırmadan gelen bir değer olmalıdır ve yukarıdaki
SuccessRehashNeeded deseni gibi, kullanıcı bir dahaki girişinde özet
yeni parametrelerle yeniden üretilmelidir.
Node'a özgü tuzak — 12-10'un devamı. Parola hash'leme CPU-yoğun bir
iştir ve bcrypt/argon2 paketlerinin asenkron sürümleri işi
libuv iş parçacığı havuzunda yapar. O havuzun varsayılan boyutu
4'tür ve dosya okuma, DNS gibi işlerle paylaşılır. Sonuç: aynı anda 4'ten
fazla giriş isteği geldiğinde sıraya girerler, üstelik havuzu kullanan diğer işler
de bekler. Belirtisi tanıdıktır — CPU görece düşük ama gecikme uçmuş durumdadır.
Senkron sürümü (bcrypt.hashSync) kullanmak çok daha kötüdür: doğrudan
olay döngüsünü bloklar ve tüm istekler durur. Pratik yaklaşım:
asenkron sürümü kullan, UV_THREADPOOL_SIZE'ı çekirdek sayısına göre
ayarla, giriş ucuna oran sınırı koy ve maliyeti ölçerek seç. Bu üçü birleştiğinde
giriş ucu bir hizmet engelleme yüzeyi olmaktan çıkar — 1. dersteki STRIDE
tablosunda "D" harfinin somut karşılığı budur.
Parola politikası. Güncel rehberler (NIST) klasik kuralları tersine
çevirdi: uzunluk karmaşıklıktan önemlidir. Zorunlu özel karakter ve
periyodik zorunlu değiştirme, kullanıcıyı tahmin edilebilir kalıplara iter
(Parola1! → Parola2!). Bugünkü öneriler: en az 8-12
karakter alt sınır, üst sınırı düşük tutma (en az 64 karaktere izin ver),
tüm karakterlere izin ver, sızmış parola listelerine karşı kontrol et ve
periyodik zorunlu değişimi kaldır — yalnızca ihlal şüphesinde değiştirt.
05Kullanıcı numaralandırma ve zamanlama sızıntısı
Giriş ekranı iki farklı mesaj döndürüyorsa — "böyle bir kullanıcı yok" ve "parola hatalı" — saldırgana bedava bilgi vermiş olursun: hangi e-posta adreslerinin sistemde kayıtlı olduğunu tek tek doğrulayabilir. Elde ettiği liste hem hedefli kimlik avı için hem de kimlik bilgisi doldurma saldırısı için kullanılır. Bu bir güvenlik açığıdır ve adı kullanıcı numaralandırmadır.
Sızıntı yalnızca mesajdan olmaz. Üç kanalı birden kapatmak gerekir:
- Mesaj: her iki durumda da aynı metin ("Kullanıcı adı veya parola hatalı") ve aynı HTTP durum kodu.
- Zamanlama: kullanıcı yoksa kod hash doğrulaması yapmadan hemen döner — 20 ms. Kullanıcı varsa bcrypt çalışır — 200 ms. Aradaki fark ölçülebilir ve mesajdan bağımsız olarak cevabı verir. Çözüm, kullanıcı bulunamadığında da sahte bir özete karşı doğrulama çalıştırmaktır.
- Yan uçlar: kayıt ("bu e-posta zaten kayıtlı"), parola sıfırlama ("bu adres bulunamadı") ve davet uçları aynı bilgiyi sızdırır. Doğru davranış: kayıtta genel bir mesaj + e-posta ile bildirim, sıfırlamada her zaman "eğer bu adres kayıtlıysa bağlantı gönderildi" cevabı.
// Zamanlama farkını kapatan desen
const kullanici = await depo.eMailIleBul(email);
const hedefOzet = kullanici?.parolaOzeti ?? SAHTE_OZET; // önceden üretilmiş geçerli bir bcrypt özeti
const parolaTamam = await bcrypt.compare(parola, hedefOzet); // her iki durumda da AYNI maliyet
if (!kullanici || !parolaTamam) {
await gecikmeliSayacArtir(email, ip);
return yanit.status(401).json({ hata: "Kullanıcı adı veya parola hatalı" });
}
Aynı sınıftan bir ayrıntı: sabit zamanlı karşılaştırma. Jeton, API anahtarı
ya da HMAC gibi sırları === ile karşılaştırmak, ilk farklı baytta
döndüğü için karşılaştırma süresinden bilgi sızdırır. Sırlar
crypto.timingSafeEqual (Node) veya
CryptographicOperations.FixedTimeEquals (.NET) ile karşılaştırılır.
Uzaktan ölçümde gürültü yüksektir ama saldırı gösterilmiştir; maliyeti tek satır
olan bir önlem tartışılmaz.
06Kaba kuvvet, oran sınırlama ve kilitlemenin bedeli
Üç saldırı biçimi ayırt edilir, çünkü savunmaları farklıdır:
| Saldırı | Yöntem | Etkili savunma |
|---|---|---|
| Kaba kuvvet | Tek hesaba binlerce parola denemesi | Hesap bazında oran sınırı, üstel gecikme |
| Kimlik bilgisi doldurma | Başka sitelerden sızmış kullanıcı-parola çiftlerini deneme | Sızmış parola kontrolü, MFA, cihaz/konum anormalliği |
| Parola serpme | Binlerce hesaba tek bir popüler parola denemesi | IP/kaynak bazında sınır — hesap bazında sınır bunu görmez |
Üçüncü satır, oran sınırlamanın neden iki boyutlu olması gerektiğini açıklar: yalnızca hesap başına sınır koyarsan, her hesaba bir deneme yapan saldırgan hiçbir sınıra takılmaz. Yalnızca IP başına sınır koyarsan, dağıtık bir saldırgan (bot ağı) onu da aşar ve üstelik aynı kurumsal NAT arkasındaki masum kullanıcıları engellersin. Üretimde ikisi birlikte, farklı eşiklerle kullanılır.
Hesap kilitleme bir tuzaktır. "5 hatalı denemede hesabı kilitle" kuralı, saldırganın herkesin hesabını kilitleyerek sistemi kullanılamaz hâle getirmesini sağlar — kimlik doğrulama önlemi, hizmet engelleme aracına dönüşür. Daha iyi seçenekler:
- Üstel gecikme: her hatalı denemeden sonra bekleme süresini artır (1s, 2s, 4s...). Kullanıcıyı kilitlemez, saldırganı ekonomik olarak durdurur.
- Sürtünmeyi kademelendir: ilk denemeler serbest, sonra CAPTCHA, sonra e-posta ile doğrulama. Meşru kullanıcı nadiren üçüncü kademeye ulaşır.
- Kilitleme kullanılacaksa süreli olsun (15 dakika) ve kilitlenme olayı kullanıcıya bildirilsin.
Sayaç nerede tutulur? Süreç içinde tutulursa 12-10'daki cluster tuzağına düşersin: her kopya kendi sayacını tutar, N kopyada sınır fiilen N katına çıkar. Paylaşımlı sayaç gerekir (Redis, 19. hat) — ya da sınır uygulamanın önündeki ters vekilde/gateway'de uygulanır ki bu genelde daha doğru yerdir: istek uygulamaya hiç ulaşmaz, bcrypt hiç çalışmaz.
07Yetkilendirme: modeller ve en sık zafiyet
Kimlik doğrulandıktan sonra karar verilir. Üç model yaygındır:
| Model | Karar neye bakar | Uygun olduğu yer | Sınırı |
|---|---|---|---|
| RBAC (rol tabanlı) | Kullanıcının rolü | Rol sayısı az ve durağan (yönetici, denetçi, kullanıcı) | "Rol patlaması": her istisna yeni rol doğurur |
| ABAC (öznitelik/politika) | Kullanıcı, kaynak ve bağlam öznitelikleri | "Aynı kurumdan olan ve başvuru dönemi açıksa" gibi kurallar | Politikaların test edilmesi ve anlaşılması zorlaşır |
| ReBAC (ilişki tabanlı) | Kullanıcı ile kaynak arasındaki ilişki | Paylaşım grafikleri: "belgenin sahibi", "klasörün düzenleyicisi" | Ayrı bir yetki servisi/veri modeli gerektirir |
Pratikte çoğu sistem RBAC ile başlar ve zamanla ABAC kurallarıyla melezleşir —
8. derste ASP.NET Core'un policy mekanizmasının tam olarak bu geçişi
nasıl karşıladığını göreceksin.
Model seçiminden çok daha önemli olan bir şey var. Web uygulamalarında bulunan en yaygın kritik zafiyet, model hatası değil, kontrolün hiç yapılmamasıdır: IDOR — güvensiz doğrudan nesne referansı. Kod rolü kontrol eder, sahipliği kontrol etmeyi unutur.
// ZAFİYETLİ: rol doğru, sahiplik kontrolü yok
[Authorize(Roles = "BasvuruSahibi")]
[HttpGet("basvuru/{id}")]
public async Task<IActionResult> Getir(int id) {
var b = await _db.Basvurular.FindAsync(id); // id İSTEMCİDEN geliyor
return Ok(b); // /basvuru/1, /basvuru/2, /basvuru/3 ...
}
// DOĞRU: kimlik oturumdan, sorgu sahiplikle sınırlı
[Authorize(Roles = "BasvuruSahibi")]
[HttpGet("basvuru/{id}")]
public async Task<IActionResult> Getir(int id) {
var kullaniciId = User.GetKullaniciId(); // İSTEKTEN DEĞİL, doğrulanmış kimlikten
var b = await _db.Basvurular
.FirstOrDefaultAsync(x => x.Id == id && x.KullaniciId == kullaniciId);
return b is null ? NotFound() : Ok(b); // yok da olabilir, başkasının da olabilir → 404
}
Üç kural bu zafiyeti kapatır. Birincisi: kimlik asla istekten okunmaz — 1. dersteki "istemciden gelen hiçbir şeye güvenilmez" kuralının en somut biçimi. İkincisi: yetki kontrolü veriye en yakın yerde yapılır; sorgunun kendisine sahiplik koşulu koymak, "önce çek sonra kontrol et" deseninden daha güvenlidir çünkü unutulacak bir adım kalmaz. Üçüncüsü: tahmin edilebilir artan sayılar yerine tahmin edilemez kimlikler (UUID) kullanmak yüzeyi küçültür — ama bu tek başına önlem değildir, yalnızca ek katmandır: 1. dersteki "belirsizlik yoluyla güvenlik" uyarısı burada geçerlidir.
Son olarak yetkilendirmenin nerede yapıldığı sorusu. 10-13, 11-13 ve 12-08'de üç kez söylediğimiz cümle burada kapanıyor: arayüzde menü gizlemek ve router guard koymak yetkilendirme değildir, kullanıcı deneyimidir. Karar API'de verilir. 12-12'deki BFF kuralı da aynı yöne bakar: BFF kimliği taşır ama iç servisler "BFF nasılsa kontrol etmiştir" demez, her servis kendi kararını verir. Bu, derinlemesine savunmanın yetkilendirme tarafındaki karşılığıdır.
08Oturumun yaşam döngüsü
Kimlik doğrulama tek seferlik bir olaydır; ondan sonraki her istekte kullanıcının kim olduğunu hatırlatan bir mekanizma gerekir. O mekanizmanın iki biçimi vardır — sunucu tarafı oturum (4. ders) ve jeton (5. ders) — ama yaşam döngüsü kuralları ikisinde de ortaktır:
- Girişte kimlik yenilenir. Giriş öncesi verilen oturum kimliği girişten sonra kullanılmaya devam ederse oturum sabitleme zafiyeti doğar (4. derste ayrıntısı).
- İki ayrı süre vardır: hareketsizlik süresi (idle timeout — 30 dakika işlem yoksa düş) ve mutlak süre (absolute timeout — 8 saat sonra her hâlükârda düş). İkincisi olmayan sistemde çalınmış bir oturum süresiz yaşar.
- Parola değişince ve MFA sıfırlanınca tüm oturumlar düşürülür. Kullanıcı "hesabım ele geçirildi" diyerek parolasını değiştirdiğinde saldırganın oturumu hâlâ açıksa hiçbir şey çözülmemiştir. Bu, jeton tabanlı sistemlerde özel çaba gerektirir — 5. dersteki iptal problemi.
- Çıkış sunucuda gerçekleşir. Çerezi silmek ya da istemcideki jetonu atmak, sunucu tarafında hiçbir şey iptal etmiyorsa çıkış değildir.
- Aktif oturumlar görünür olmalıdır. Kullanıcıya "şu cihazlarda açık oturumun var, hepsini kapat" seçeneği sunmak, hem iyi bir kullanıcı deneyimi hem bir olay müdahale aracıdır.
09Mini alıştırma — giriş ucunu sertleştir
Yaklaşık 20 dakika · 12. hattaki Express uygulaması ya da 5. hattaki API
- Maliyeti ölç.
bcrypt.hash'i 8, 10, 12 ve 14 maliyetle çalıştırıp süreleri yazdır. Kendi makinende 100-250 ms'e denk gelen değeri bul ve neden bu aralığı hedeflediğini bir cümleyle yaz. - Havuzu doldur. Maliyeti 12'de tut, giriş ucuna aynı anda 8 istek at
(
npx autocannon -c 8 -a 8). Yanıt sürelerinin dağılımına bak; sonraUV_THREADPOOL_SIZE=8ile yeniden başlatıp aynı testi tekrarla. İki sonucu yan yana koy — 12-10'daki "hangi kaynak doymuş" tablosunda bunun hangi satır olduğunu belirt. - Numaralandırmayı kanıtla. Var olmayan bir kullanıcıyla ve var olan bir
kullanıcı + yanlış parolayla giriş dene;
curl -w "%{time_total}"ile süreleri karşılaştır. Farkı gördükten sonra sahte özet desenini uygula ve sürelerin eşitlendiğini doğrula. - Oran sınırı koy. Giriş ucuna iki boyutlu sınır ekle: IP başına dakikada 20, hesap başına dakikada 5. Parola serpme senaryosunu taklit et — 50 farklı kullanıcı adına aynı parolayı dene ve yalnızca hesap bazlı sınırın bunu yakalamadığını gözle.
- IDOR avı. Kendi API'nde kimlik parametresi alan tüm uçları listele. Her biri için sor: sorgu sahiplikle sınırlı mı, yoksa "önce çek sonra bak" mı? Bir uçta zafiyeti bilerek bırak, ikinci bir kullanıcı hesabıyla başkasının kaydını çek, sonra düzelt. Düzeltmeyi koruyan bir negatif test yaz: "başka kullanıcının kaydına 404 döner".
10Kendini sına
AuthN ile AuthZ farkı nedir; 401, 403 ve 404 hangi durumda döner? İstemci tarafında bu ayrım neyi değiştirir?
AuthN "sen kimsin?" — kanıt alır, kimlik üretir. AuthZ "buna hakkın var mı?" — doğrulanmış kimliğin belirli bir kaynakta belirli bir işlemi yapıp yapamayacağının kararı. Sıra değişmez. 401 Unauthorized ismine rağmen kimlik doğrulama hatasıdır: kimlik yok ya da geçersiz, giriş yapmak durumu değiştirir. 403 Forbidden yetkilendirme hatasıdır: kimlik geçerli ama hak yok — tekrar giriş yapmak bir şey değiştirmez. 404, kaynağın varlığının kendisi gizli olduğunda 403 yerine bilinçli olarak seçilir; aksi hâlde saldırgan kimlikleri tek tek deneyerek hangi kayıtların var olduğunu haritalar. İstemcide fark kritiktir: araya girici 401'de kullanıcıyı giriş ekranına yollar, 403'te yollamaz — 403'te yollamak sonsuz döngü üretir.
Parolalar neden SHA-256 ile saklanmaz? Tuz, biber ve maliyet parametresi ne işe yarar, maliyet nasıl seçilir?
SHA-256 hızlı olmak için tasarlanmıştır; bir GPU saniyede milyarlarcasını hesaplar, sızan tablo sözlük saldırısıyla saatlerde çözülür. Ayrıca tuzsuz hash'te aynı parolayı kullananların özeti aynı çıkar ve gökkuşağı tabloları aramayı bedavaya indirir. Tuz kullanıcı başına rastgele bir değerdir, özetin yanında açıkça saklanır (gizli olması gerekmez); her hesabı ayrı bir problem yapar ve önceden hesaplanmış tabloları geçersiz kılar. Biber tüm kullanıcılar için ortak, veritabanında değil uygulama sırlarında duran ek değerdir; yalnızca veritabanı sızarsa saldırıyı imkânsızlaştırır, bedeli döndürmenin zorluğudur. Maliyet (bcrypt iş faktörü, Argon2 bellek/yineleme) hash'lemeyi bilerek pahalı ve ayarlanabilir yapar. Seçim ölçümle: kendi üretim donanımında tek doğrulama ~100-250 ms sürmeli. Tercih sırası Argon2id > scrypt > bcrypt (72 bayt girdi sınırına dikkat) > PBKDF2; parametre yapılandırmadan gelir ve kullanıcı girişinde özet yeni parametrelerle yeniden üretilir.
Node'da parola hash'leme hangi kaynağı doyurur, belirtisi nedir? Kullanıcı numaralandırma hangi üç kanaldan sızar?
Asenkron bcrypt/argon2 işi
libuv iş parçacığı havuzunda yapar; havuz varsayılan 4 genişliğindedir ve
dosya/DNS işleriyle paylaşılır. Aynı anda 4'ten fazla giriş geldiğinde istekler sıraya
girer ve havuzu kullanan başka işler de bekler — belirti 12-10'daki teşhis tablosundan
tanıdıktır: CPU görece düşük ama gecikme uçmuş. Senkron sürüm daha kötüdür,
doğrudan olay döngüsünü bloklar ve tüm istekler durur. Önlem: asenkron sürüm,
UV_THREADPOOL_SIZE ayarı, giriş ucunda oran sınırı, ölçülmüş maliyet.
Numaralandırma üç kanaldan sızar: (1) mesaj — "kullanıcı yok" ile "parola
yanlış" ayrımı; ikisi de aynı metin ve aynı durum kodu olmalı; (2) zamanlama —
kullanıcı yoksa hash hiç çalışmaz, 20 ms'e karşı 200 ms; çözüm, kullanıcı
bulunamadığında da önceden üretilmiş sahte bir özete karşı doğrulama
çalıştırmak; (3) yan uçlar — kayıt, parola sıfırlama ve davet uçları; sıfırlama
her zaman "eğer bu adres kayıtlıysa bağlantı gönderildi" demeli. Sır karşılaştırmaları
ayrıca timingSafeEqual / FixedTimeEquals ile sabit zamanlı
yapılır.
Hesap kilitleme neden tuzaktır; parola serpme saldırısını hangi sınır yakalar? IDOR nedir ve üç kuralla nasıl kapatılır?
"5 hatalı denemede kilitle" kuralı, saldırganın herkesin hesabını
kilitleyerek sistemi kullanılamaz hâle getirmesine izin verir — kimlik doğrulama
önlemi hizmet engelleme aracına dönüşür. Daha iyisi: üstel gecikme, kademeli
sürtünme (serbest → CAPTCHA → e-posta doğrulama) ve kullanılacaksa süreli kilit.
Parola serpme binlerce hesaba tek bir popüler parola dener; hesap başına sınır
bunu hiç görmez, yakalayan şey IP/kaynak bazlı sınırdır — bu yüzden
sınır iki boyutludur. Sayaç süreç içinde tutulursa cluster'da her kopya kendi sayacını
tutar ve sınır N katına çıkar; paylaşımlı sayaç ya da vekilde uygulama gerekir.
IDOR, rolün kontrol edilip sahipliğin kontrol edilmemesidir:
/basvuru/{id} ucunda id istemciden gelir ve kod onu doğrudan çeker. Üç
kural: (1) kimlik asla istekten değil doğrulanmış oturumdan okunur; (2) yetki
kontrolü veriye en yakın yerde, tercihen sorgunun koşuluna yazılır — "önce çek
sonra kontrol et" unutulabilir bir adım bırakır; (3) tahmin edilemez kimlikler (UUID)
yüzeyi küçültür ama tek başına önlem değildir, yalnızca ek katmandır.
11Sınav köşesi
Yazılıda nasıl sorulur
(1) Authentication ile authorization arasındaki farkı açıklayın. (2) HTTP 401 ve 403 hangi durumlarda döner? (3) Çok faktörlü doğrulama nedir, faktör sınıfları nelerdir? (4) Parolalar veritabanında nasıl saklanmalıdır? (5) Salt nedir, neden gereklidir? (6) bcrypt/Argon2 gibi algoritmalar neden bilerek yavaştır? (7) Rainbow table saldırısı nedir? (8) Brute force, credential stuffing ve password spraying farkları nelerdir? (9) Rate limiting neden hem IP hem hesap bazında yapılır? (10) Hesap kilitlemenin riski nedir? (11) User enumeration nedir, nasıl önlenir? (12) RBAC ve ABAC modellerini karşılaştırın. (13) IDOR zafiyeti nedir, nasıl önlenir? (14) Session fixation nedir?
Sözlüde nasıl anlatılır — 2 dakikalık taslak
Düzen: (1) Ayrım — "Kimlik doğrulama 'sen kimsin', yetkilendirme 'buna hakkın var mı'. Sırası değişmez; HTTP'de karşılıkları 401 ve 403, ve istemcide bu ayrım önemlidir: 401'de giriş ekranına yollarım, 403'te yollamam." (2) Parola — "Parolayı geri döndürülebilir saklamam. SHA-256 yanlıştır çünkü hızlı olmak için tasarlanmıştır; Argon2id ya da bcrypt kullanırım, tuz kullanıcı başına rastgeledir ve gizli olması gerekmez, maliyeti kendi donanımımda 100-250 ms'e göre ölçerek seçerim." (3) Node tuzağı — "Bunun Node tarafında bir bedeli var: hash libuv havuzunda çalışır, havuz varsayılan 4'tür; giriş ucu oran sınırsız bırakılırsa CPU düşükken gecikme uçar — yani giriş ucu bir DoS yüzeyidir." (4) Numaralandırma — "'Kullanıcı yok' ile 'parola yanlış' ayrımını hiçbir kanaldan sızdırmam: aynı mesaj, aynı kod, ve aynı süre — kullanıcı yoksa da sahte bir özete karşı doğrulama çalıştırırım. Sıfırlama ucu her zaman aynı cevabı verir." (5) Kıdem vuruşu — "Yetkilendirmede model seçiminden önce gelen şey nesne düzeyi kontroldür: en sık zafiyet IDOR'dur ve sebebi rolün kontrol edilip sahipliğin unutulmasıdır. Kimliği istekten değil oturumdan okurum, sahiplik koşulunu sorgunun içine yazarım, ve bunu koruyan bir negatif test bırakırım. Arayüzdeki guard'lar ve gizli menüler yetkilendirme değildir; karar API'de verilir ve her servis kendi kararını verir."
