Kısaca cevap
JavaScript şifre gücü ölçmek için yalnızca "büyük harf, rakam, sembol var mı" diye bakmak yetmez: "Parola123!" bu testi geçer ama saldırganların ilk denediği kalıptır. Doğru yaklaşım, şifrenin entropisini tahmin ederken yaygın kelimeleri, dizileri ve yılları "ucuz" saymak, isteğe bağlı olarak Have I Been Pwned ile sızıntı kontrolü yapmak ve sonucu aria-live ile ekran okuyuculara da duyurmaktır. Aşağıdaki kodlar Node.js 24'te test edildi.
Bu sayfada
Karakter türü saymak neden yanıltıcı?
İnternette en sık görülen JavaScript şifre gücü örnekleri, şifrede küçük harf, büyük harf, rakam ve sembol olup olmadığını sayıp her biri için bir puan verir. Bu yaklaşım kolaydır ama iki büyük hata yapar:
- Kalıbı görmez. "Parola123!" dört türü de içerdiği için tam puan alır. Oysa saldırganların deneme listeleri tam olarak "kelime + 123 + !" gibi kalıplardan oluşur.
- Uzunluğu küçümser. Yalnızca küçük harflerden oluşan, rastgele seçilmiş 20 karakterlik bir şifre, 8 karakterlik "karışık" bir şifreden çok daha güçlüdür; ama tür sayan ölçer onu "zayıf" gösterir.
NIST SP 800-63B'nin güncel sürümü de karakter türü karıştırma gibi kompozisyon kuralları konulmamasını, bunun yerine şifrenin yaygın, beklenen veya sızmış şifrelerden oluşan bir listeyle karşılaştırılmasını ve kullanıcıya güçlü şifre seçmesi için yol gösterilmesini ister. İyi bir güç göstergesi bu yol göstermenin parçasıdır.
1. Adım: Saf entropi tahmini
Rastgele üretilmiş bir şifrenin entropisi uzunluk × log2(havuz büyüklüğü) formülüyle hesaplanır. Havuz, şifrede görülen karakter türlerinden tahmin edilir: 26 küçük harf, 26 büyük harf, 10 rakam ve klavyedeki 32 sembol. Formülün arkasındaki matematiği Olasılık ve Rastgelelik yazısında anlattık.
// 1) Saf entropi: yalnızca uzunluk ve karakter türlerine bakar
function havuzBoyutu(sifre) {
let havuz = 0;
if (/[a-z]/.test(sifre)) havuz += 26;
if (/[A-Z]/.test(sifre)) havuz += 26;
if (/[0-9]/.test(sifre)) havuz += 10;
if (/[^a-zA-Z0-9]/.test(sifre)) havuz += 32;
return havuz;
}
function safEntropi(sifre) {
const havuz = havuzBoyutu(sifre);
return havuz ? sifre.length * Math.log2(havuz) : 0;
}
Bu işlev, şifrenin rastgele üretildiğini varsayar. Türkçe harfler gibi ASCII dışı karakterler sembol havuzuna sayılır; basitlik için bu kadarı yeterli. Asıl sorun, insanların şifreleri rastgele seçmemesidir.
2. Adım: Sözlük ve kalıp kontrolü
Saldırganlar şifreleri harf harf değil, parça parça tahmin eder: önce yaygın şifreler ve kelimeler, sonra bunların sonuna eklenen yıllar, sayı dizileri ve semboller. Ölçer de aynı gözle bakmalıdır. Aşağıdaki kod şifreyi küçük harfe çevirir, Türkçe harfleri Latin karşılıklarına indirir, "@ → a", "0 → o" gibi değişiklikleri çözer ve bulduğu her kalıbı, saldırganın onu tahmin etmek için harcayacağı yaklaşık deneme sayısı kadar puanlar. Geriye kalan karakterler rastgele kabul edilir.
// 2) Kalıp kontrolü: bilinen kelimeleri, dizileri ve yılları "ucuz" sayar
const YAYGIN = ["123456", "password", "qwerty", "asdfgh", "zxcvbn", "parola", "sifre",
"admin", "iloveyou", "askim", "galatasaray", "fenerbahce", "besiktas", "trabzonspor",
"istanbul", "ankara", "izmir", "turkiye", "mehmet", "ayse", "fatma", "ahmet"];
const LEET = { "@": "a", "4": "a", "3": "e", "1": "i", "!": "i", "0": "o", "$": "s", "5": "s", "7": "t" };
const TR = { "ç": "c", "ğ": "g", "ı": "i", "ö": "o", "ş": "s", "ü": "u" };
function sadelestir(sifre) {
return [...sifre.toLocaleLowerCase("tr")].map(c => TR[c] ?? c).join("");
}
function kalipAnalizi(sifre) {
let kalan = sadelestir(sifre);
let bit = 0;
const uyarilar = [];
const isaretle = (bas, uzunluk, maliyet, uyari) => {
kalan = kalan.slice(0, bas) + "\u0000".repeat(uzunluk) + kalan.slice(bas + uzunluk);
bit += maliyet;
uyarilar.push(uyari);
};
// Yaygın kelimeler (leetspeak çözülmüş hâliyle de aranır)
const leetsiz = [...kalan].map(c => LEET[c] ?? c).join("");
for (const kelime of YAYGIN) {
const i = leetsiz.indexOf(kelime);
if (i !== -1 && !kalan.slice(i, i + kelime.length).includes("\u0000")) {
isaretle(i, kelime.length, Math.log2(YAYGIN.length) + 2, `Yaygın kelime: "${kelime}"`);
}
}
// 19xx / 20xx yılları
for (const m of kalan.matchAll(/(19|20)\d\d/g)) {
isaretle(m.index, 4, Math.log2(140), `Yıl: ${m[0]}`);
}
// Ardışık (abc, 1234, 9876) ve tekrar eden (aaa) karakterler
for (let i = 0; i + 2 < kalan.length; i++) {
const a = kalan.charCodeAt(i), b = kalan.charCodeAt(i + 1);
const fark = b - a;
if (kalan[i] === "\u0000" || Math.abs(fark) > 1) continue;
let j = i + 1;
while (j + 1 < kalan.length && kalan.charCodeAt(j + 1) - kalan.charCodeAt(j) === fark && kalan[j + 1] !== "\u0000") j++;
const uzunluk = j - i + 1;
if (uzunluk >= 3) {
isaretle(i, uzunluk, Math.log2(havuzBoyutu(sifre[i])) + Math.log2(uzunluk) + 1,
fark === 0 ? "Tekrar eden karakterler" : "Ardışık karakterler");
i = j;
}
}
// Geriye kalan karakterler rastgele sayılır
const serbest = [...kalan].map((c, i) => (c === "\u0000" ? "" : sifre[i])).join("");
bit += serbest.length * Math.log2(havuzBoyutu(serbest) || 1);
return { bit: Math.round(bit), uyarilar };
}
function seviye(bit) {
if (bit < 40) return "Zayıf";
if (bit < 60) return "Orta";
if (bit < 80) return "İyi";
return "Çok güçlü";
}
Kelime listesi burada bilerek kısa tutuldu. Gerçek bir uygulamada binlerce yaygın şifre ve kelime içeren bir liste kullanmalısınız; liste büyüdükçe bir kelimenin "maliyeti" de log2(liste boyu) olarak artar. Seviye eşikleri yuvarlak değerlerdir: Şifre Kaç Karakter Olmalı? yazısında anlattığımız gibi 60 bitin altı hızlı saldırılara karşı zayıf, 80 bit ve üstü pratikte kırılamaz kabul edilir.
Node.js'de deneme
Kodu bir dosyaya kaydedip sonuna aşağıdaki satırları ekleyerek node ile çalıştırabilirsiniz:
for (const s of ["Parola123!", "Galatasaray1905", "qwerty12", "Kx9#mPq2vL7!"]) {
const { bit, uyarilar } = kalipAnalizi(s);
console.log(s, "| saf:", safEntropi(s).toFixed(1), "| kalıp:", bit, seviye(bit), uyarilar);
}
Parola123! | saf: 65.5 | kalıp: 17 Zayıf [ 'Yaygın kelime: "parola"', 'Ardışık karakterler' ]
Galatasaray1905 | saf: 89.3 | kalıp: 14 Zayıf [ 'Yaygın kelime: "galatasaray"', 'Yıl: 1905' ]
qwerty12 | saf: 41.4 | kalıp: 13 Zayıf [ 'Yaygın kelime: "qwerty"' ]
Kx9#mPq2vL7! | saf: 78.7 | kalıp: 79 İyi []
Fark açık: tür sayan yaklaşım "Galatasaray1905"e en yüksek puanı verirken, kalıp kontrolü onu bir takım adı ve kuruluş yılı olarak yakalar. Rastgele üretilmiş "Kx9#mPq2vL7!" ise iki yöntemde de aynı sonucu alır, çünkü içinde tahmin edilebilir bir parça yoktur.
3. Adım: Sızıntı kontrolü (Have I Been Pwned)
Bir şifre kalıp içermese bile daha önce bir sızıntıda yayımlanmışsa saldırganların listelerindedir. Have I Been Pwned servisinin Pwned Passwords API'si bunu şifreyi göndermeden kontrol etmenizi sağlar. Resmi API belgesine göre yöntem şöyle işler:
- Şifrenin SHA-1 özeti tarayıcıda hesaplanır ve yalnızca ilk 5 karakteri
https://api.pwnedpasswords.com/range/{önek}adresine gönderilir (k-anonimlik). - Servis bu önekle başlayan bütün özetlerin kalan 35 karakterini ve her birinin sızıntılarda kaç kez görüldüğünü
SONEK:ADETbiçiminde döndürür; eşleştirme sizin tarafınızda yapılır. - API anahtar ve abonelik gerektirmez, CORS destekler.
Add-Padding: truebaşlığı, yanıt boyutundan bilgi sızmasın diye yanıta rastgele sayıda sahte satır ekler (toplam 800–1.000 kayıt); bu satırların adedi 0'dır.
// 3) Sızıntı kontrolü: HIBP Pwned Passwords, k-anonimlik
async function sizintiSayisi(sifre) {
const ozet = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(sifre));
const hex = [...new Uint8Array(ozet)]
.map(b => b.toString(16).padStart(2, "0")).join("").toUpperCase();
const onEk = hex.slice(0, 5), sonEk = hex.slice(5);
const yanit = await fetch(`https://api.pwnedpasswords.com/range/${onEk}`,
{ headers: { "Add-Padding": "true" } });
if (!yanit.ok) throw new Error(`HIBP yanıtı: ${yanit.status}`);
for (const satir of (await yanit.text()).split("\n")) {
const [ek, adet] = satir.trim().split(":");
if (ek === sonEk) return Number(adet);
}
return 0;
}
Bu işlevi 29 Eylül 2026'da denediğimizde "Parola123!" için 41.921, "P@ssw0rd" için 6.421.042 sonucu döndü; rastgele üretilmiş bir şifre için ise 0. Sayılar veri kümesi güncellendikçe değişir. Sızıntı kontrolünü her tuş vuruşunda değil, kullanıcı bir düğmeye bastığında veya formu gönderirken yapın.
Bu kontrol, şifre özetinin bir parçasını üçüncü taraf bir servise gönderir; kullanıcılarınıza bunu gizlilik metninizde açıklayın. Sitemizdeki Şifre Güç Testi aracı ise hiçbir istek göndermez, yalnızca tarayıcıda kalıp analizi yapar.
4. Adım: Erişilebilir gösterge
Güç göstergesi yalnızca renkli bir çubuksa, renk ayırt edemeyen ya da ekran okuyucu kullanan biri için anlam taşımaz. Aşağıdaki örnekte sonuç hem bir <meter> öğesinde hem de metin olarak verilir. Metin kutusu role="status" ve aria-live="polite" taşıdığı için ekran okuyucular değişikliği, kullanıcının yazmasını bölmeden okur. Değerlendirme her tuşta değil, yazma 400 ms durduktan sonra yapılır; böylece ekran okuyucu her harfte konuşmaz.
<label for="sifre">Şifre</label>
<input id="sifre" type="password" autocomplete="new-password">
<meter id="olcek" min="0" max="100" low="40" high="80" optimum="100" value="0"
aria-label="Şifre gücü"></meter>
<p id="sonuc" role="status" aria-live="polite"></p>
<button id="kontrol" type="button">Sızıntılarda var mı?</button>
<script src="olcer.js"></script>
<script>
const girdi = document.getElementById("sifre");
const olcek = document.getElementById("olcek");
const sonuc = document.getElementById("sonuc");
let zamanlayici;
girdi.addEventListener("input", () => {
clearTimeout(zamanlayici);
zamanlayici = setTimeout(() => {
const sifre = girdi.value;
if (!sifre) { olcek.value = 0; sonuc.textContent = ""; return; }
const { bit, uyarilar } = kalipAnalizi(sifre);
olcek.value = Math.min(bit, 100);
sonuc.textContent = `${seviye(bit)}: yaklaşık ${bit} bit. ${uyarilar.join(". ")}`;
}, 400);
});
document.getElementById("kontrol").addEventListener("click", async () => {
try {
const adet = await sizintiSayisi(girdi.value);
sonuc.textContent = adet
? `Bu şifre bilinen sızıntılarda ${adet.toLocaleString("tr-TR")} kez görülmüş. Kullanmayın.`
: "Bu şifre bilinen sızıntılarda bulunamadı.";
} catch {
sonuc.textContent = "Sızıntı kontrolü şu anda yapılamadı.";
}
});
</script>
Bu örneği Node.js'de basit bir sahte DOM ile de sınadık: art arda iki girişte yalnızca sonuncusu değerlendirildi, alan boşaltılınca gösterge sıfırlandı ve sızıntı düğmesi yukarıdaki sonuçları metin olarak yazdı. Birkaç ek öneri:
autocomplete="new-password"kullanın ve yapıştırmayı engellemeyin; şifre yöneticisi kullananlar uzun, rastgele şifreler yapıştırır.- Sonuç metninde neyin sorun olduğunu söyleyin ("Yaygın kelime: parola"). Yalnızca "Zayıf" demek kullanıcıya yol göstermez.
- Tarayıcıdaki kontrol yalnızca yol göstericidir; kullanıcı onu atlayabilir. Yasaklı şifre listesi kontrolünü sunucuda da yapın ve şifreleri bcrypt veya Argon2 ile saklayın.
Şifre gücü ölçerde sık yapılan hatalar
- Şifreyi sunucuya göndermek: Gücü ölçmek için şifreyi her tuşta bir API'ye yollamak, onu kayıtlara (log) düşürme riski taşır. Ölçümü tarayıcıda yapın; sunucuya şifre yalnızca form gönderildiğinde, HTTPS üzerinden gitsin.
- Yalnızca renge güvenmek: Kırmızı-yeşil çubuk, renk körlüğü olan kullanıcılar için ayırt edilemez. Seviyeyi mutlaka metinle de yazın.
- Kullanıcıyı kalıba zorlamak: "En az bir sembol şart" gibi kurallar kullanıcıları "Parola1!" gibi tahmin edilebilir şifrelere iter. Uzunluğu ödüllendirin, yasaklı listeyi uygulayın.
- Üst sınırı düşük tutmak: Şifre alanına 16 veya 20 karakter sınırı koymak, uzun parola cümlelerini engeller. NIST, şifrelerin en az 64 karaktere kadar kabul edilmesini önerir.
Hazır kütüphane: zxcvbn
Kendi ölçerinizi yazmak, mantığı anlamak için iyi bir egzersizdir. Daha kapsamlı bir tahmin istiyorsanız Dropbox'un açık kaynak zxcvbn kütüphanesine bakabilirsiniz. Resmi deposuna göre şifre kırma araçlarından esinlenir; yaygın şifreleri, isim ve kelime listelerini, tarihleri, tekrarları, dizileri, klavye kalıplarını ve harf yerine rakam kullanımını tanır. 0–4 arası puanın yanında tahmini deneme sayısı, kırılma süresi tahminleri ve kullanıcıya gösterilecek öneriler döndürür. MIT lisanslıdır; belgesine göre sıkıştırılmış boyutu yaklaşık 400 kB olduğu için yalnızca şifre alanı olan sayfalarda yüklenmesi mantıklıdır.
Rastgele şifre üretme tarafını JavaScript ile güvenli rastgele şifre üretme yazısında crypto.getRandomValues() ile anlattık. Kendi şifrenizi denemek isterseniz Şifre Güç Testi aracını kullanabilirsiniz.
Sık sorulan sorular
JavaScript ile şifre kontrolü nasıl yapılır?
Şifre alanının input olayını dinleyip değeri bir değerlendirme işlevine verirsiniz. Bu işlev uzunluğa ve karakter çeşitliliğine ek olarak yaygın kelimeleri, sayı dizilerini, yılları ve tekrarları aramalı; sonucu metin olarak göstermelidir. Tarayıcıdaki kontrol yalnızca kullanıcıya yol göstermek içindir, asıl kural sunucuda da uygulanmalıdır.
Şifre güç göstergesi nedir?
Kullanıcı şifresini yazarken şifrenin ne kadar kolay tahmin edilebileceğini çubuk ve metinle gösteren arayüz öğesidir. İyi bir gösterge yalnızca "büyük harf var mı" diye bakmaz, saldırganların önce denediği kelime ve kalıpları da hesaba katar ve neyin sorun olduğunu açıklar.
Büyük harf, rakam ve sembol içeren şifre güçlü müdür?
Her zaman değil. "Parola123!" dört karakter türünü de içerir ama bir sözlük kelimesi, sıralı rakamlar ve sona eklenmiş bir ünlemden oluşur; saldırganlar tam olarak bu kalıbı önce dener. NIST SP 800-63B de karakter türü karıştırma zorunluluğu getirilmemesini, bunun yerine şifrelerin yaygın ve sızmış şifre listeleriyle karşılaştırılmasını ister.
Have I Been Pwned şifre kontrolü şifremi gönderir mi?
Hayır, k-anonimlik yöntemiyle yalnızca şifrenin SHA-1 özetinin ilk 5 karakteri gönderilir. Servis bu önekle başlayan tüm özetlerin kalan kısmını döndürür, karşılaştırma tarayıcıda yapılır. Resmi belgeye göre Pwned Passwords API'si anahtar gerektirmez ve tarayıcıdan çağrılabilir.
zxcvbn nedir, kullanmalı mıyım?
zxcvbn, Dropbox'un açık kaynak (MIT lisanslı) şifre gücü tahmin kütüphanesidir. Yaygın şifre ve isim listelerini, klavye dizilerini, tarihleri, tekrarları ve harf yerine rakam kullanımını tanır ve 0–4 arası puan verir. Resmi belgesine göre sıkıştırılmış hâli yaklaşık 400 kB'tır; bu boyut sizin için sorun değilse, kendi yazacağınız basit bir ölçerden daha kapsamlı sonuç verir.



