Merhabalar sevgili TurkHackTeam üyeleri, bu konumda sizlere kullandığınız şifrelerin ne kadar sağlam olduğunu test eden ufak bir sistemi göstereceğim
şifren sağlam mı sistemi hali hazırda kullandığınız ya da kullanmayı düşündüğünüz şifrelerin ne kadar güvenli olduğunu, brute force yöntemi ile ne kadar sürede kırılabileceğini ve pwnedpasswords veritabanında bulunup bulunmadığını kontrol eder.
Parça parça kodları inceleyerek sistemi açıklayacağım, konunun sonunda kodun tamamını indirebilirsiniz.
şifren sağlam mı sistemi hali hazırda kullandığınız ya da kullanmayı düşündüğünüz şifrelerin ne kadar güvenli olduğunu, brute force yöntemi ile ne kadar sürede kırılabileceğini ve pwnedpasswords veritabanında bulunup bulunmadığını kontrol eder.
Parça parça kodları inceleyerek sistemi açıklayacağım, konunun sonunda kodun tamamını indirebilirsiniz.
HTML:
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>şifren sağlam mı?</title>
<link href='https://fonts.googleapis.com/css?family=Poppins' rel='stylesheet'>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Poppins';
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background-color: #fff;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
width: 400px;
text-align: center;
}
h2 {
margin-bottom: 20px;
color: #333;
}
.password-box {
position: relative;
}
input[type="password"] {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 5px;
font-size: 16px;
}
.str-bar {
margin-top: 10px;
width: 100%;
height: 10px;
background-color: #ddd;
border-radius: 5px;
}
.str-i {
height: 100%;
width: 0%;
background-color: red;
border-radius: 5px;
transition: width 0.3s ease;
}
.score {
margin-top: 10px;
font-size: 16px;
color: #333;
}
.bf-time {
font-size: 14px;
color: #555;
}
.requirements {
list-style-type: none;
padding: 10px 0;
text-align: left;
color: #555;
}
.requirement {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.requirement.valid {
color: green;
}
.requirement.invalid {
color: red;
}
.pwned-warning-ok {
font-size: 14px;
color: green;
}
.pwned-warning-no {
font-size: 14px;
color: red;
}
</style>
</head>
Üst tarafta bulunan kod sayfanın head ve CSS içeriklerini barındırıyor.
HTML:
<div class="container">
<h2>şifren sağlam mı?</h2>
<div class="password-box">
<input type="password" id="password" placeholder="şifreni gir" oninput="checkPassword()">
<div class="str-bar">
<div class="str-i" id="str-i"></div>
</div>
<div class="score">şifre sağlamlığı: <span id="score">0</span>/100</div>
<div class="bf-time" id="bf-time">bruteforce dayanıklılığı: 0 saniye</div>
<div class="pwned-warning-ok" id="pwned-warning">bu şifre sızdırılmamış</div>
<ul class="requirements">
<li id="length" class="requirement">en az 8 karakter kullan</li>
<li id="uppercase" class="requirement">en az bir büyük harf kullan</li>
<li id="lowercase" class="requirement">en az bir küçük harf kullan</li>
<li id="number" class="requirement">en az bir rakam kullan</li>
<li id="special" class="requirement">en az bir özel karakter kullan</li>
<li id="repeat" class="requirement">aynı karakterleri tekrarlama</li>
<li id="seq" class="requirement">artan/azalan dizi (123, abc) kullanma</li>
</ul>
</div>
</div>
body tagini açtıktan sonra bir container açıp, kullanıcıdan şifre girmesi için bir input alanı oluşturuyoruz ve bu inputa her veri girişi yapıldığında JS kodlarımızda bulunan checkPassword() fonksiyonunu çalıştırıyoruz. Bunun yanı sıra id verilen div ve li taglerinin de kontrol ve değişikliklerini yine JS kodlarımız yardımıyla güncelliyoruz.
JavaScript:
<script>
async function checkPassword() {
const password = document.getElementById("password").value;
const strI = document.getElementById("str-i");
const scoreText = document.getElementById("score");
const bruteForceTimeText = document.getElementById("bf-time");
const pwnedWarning = document.getElementById('pwned-warning');
const lengthReq = document.getElementById("length");
const uppercaseReq = document.getElementById("uppercase");
const lowercaseReq = document.getElementById("lowercase");
const numberReq = document.getElementById("number");
const specialReq = document.getElementById("special");
const repeatReq = document.getElementById("repeat");
const seqReq = document.getElementById("seq");
let strength = 0;
let charsetSize = 0;
// input veri doluluğu sorgusu
if (password.length === 0) {
lengthReq.classList.remove("valid", "invalid");
uppercaseReq.classList.remove("valid", "invalid");
lowercaseReq.classList.remove("valid", "invalid");
numberReq.classList.remove("valid", "invalid");
specialReq.classList.remove("valid", "invalid");
repeatReq.classList.remove("valid", "invalid");
seqReq.classList.remove("valid", "invalid");
strI.style.width = "0%";
strI.style.backgroundColor = "#ddd";
scoreText.textContent = "0";
bruteForceTimeText.textContent = "bruteforce dayanıklılığı: 0 saniye";
pwnedWarning.textContent = "bu şifre sızdırılmamış";
pwnedWarning.classList.remove('pwned-warning-no');
pwnedWarning.classList.add('pwned-warning-ok');
return;
}
// şifre uzunluğu sorgusu
if (password.length >= 8) {
lengthReq.classList.add("valid");
lengthReq.classList.remove("invalid");
strength += 20;
} else {
lengthReq.classList.add("invalid");
lengthReq.classList.remove("valid");
}
// büyük harf sorgusu
if (/[A-Z]/.test(password)) {
uppercaseReq.classList.add("valid");
uppercaseReq.classList.remove("invalid");
strength += 15;
charsetSize += 26;
} else {
uppercaseReq.classList.add("invalid");
uppercaseReq.classList.remove("valid");
}
// küçük harf sorgusu
if (/[a-z]/.test(password)) {
lowercaseReq.classList.add("valid");
lowercaseReq.classList.remove("invalid");
strength += 15;
charsetSize += 26;
} else {
lowercaseReq.classList.add("invalid");
lowercaseReq.classList.remove("valid");
}
// rakam sorgusu
if (/[0-9]/.test(password)) {
numberReq.classList.add("valid");
numberReq.classList.remove("invalid");
strength += 15;
charsetSize += 10;
} else {
numberReq.classList.add("invalid");
numberReq.classList.remove("valid");
}
// özel karakter sorgusu
if (/[\W_]/.test(password)) {
specialReq.classList.add("valid");
specialReq.classList.remove("invalid");
strength += 15;
charsetSize += 32;
} else {
specialReq.classList.add("invalid");
specialReq.classList.remove("valid");
}
// tekrar karakter sorgusu
if (!/(.)\1\1/.test(password)) {
repeatReq.classList.add("valid");
repeatReq.classList.remove("invalid");
strength += 10;
} else {
repeatReq.classList.add("invalid");
repeatReq.classList.remove("valid");
}
// artan/azalan karakter sorgusu
if (!/123|abc|987|zyx/.test(password.toLowerCase())) {
seqReq.classList.add("valid");
seqReq.classList.remove("invalid");
strength += 10;
} else {
seqReq.classList.add("invalid");
seqReq.classList.remove("valid");
}
// güç durumu ve puan barı sorgusu
if (strength === 100) {
strI.style.backgroundColor = "green";
} else if (strength >= 60) {
strI.style.backgroundColor = "orange";
} else {
strI.style.backgroundColor = "red";
}
strI.style.width = strength + "%";
scoreText.textContent = strength;
// bruteforce süre hesaplaması
const combinations = Math.pow(charsetSize, password.length);
const attemptsPerSecond = 1e9; // deneme/saniye (1 milyar)
const seconds = combinations / attemptsPerSecond;
const timeToCrack = formatTime(seconds);
bruteForceTimeText.textContent = `bruteforce dayanıklılığı: ${timeToCrack}`;
// pwnedpasswords sorgusu
const pwnedCount = await checkPwnedPassword(password);
if (pwnedCount > 0) {
pwnedWarning.textContent = "şifre sızdırılmış!";
pwnedWarning.classList.remove('pwned-warning-ok');
pwnedWarning.classList.add('pwned-warning-no');
} else {
pwnedWarning.textContent = "şifre sızdırılmamış";
pwnedWarning.classList.remove('pwned-warning-no');
pwnedWarning.classList.add('pwned-warning-ok');
}
}
// zaman formatı
function formatTime(seconds) {
const years = Math.floor(seconds / (365 * 24 * 3600));
const days = Math.floor((seconds % (365 * 24 * 3600)) / (24 * 3600));
const hours = Math.floor((seconds % (24 * 3600)) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const sec = Math.floor(seconds % 60);
if (years > 0) {
return `${years} yıl ${days} gün`;
} else if (days > 0) {
return `${days} gün ${hours} saat`;
} else if (hours > 0) {
return `${hours} saat ${minutes} dakika`;
} else if (minutes > 0) {
return `${minutes} dakika ${sec} saniye`;
} else {
return `${sec} saniye`;
}
}
// pwnedpasswords apisi ile girilen şifreyi kontrol etme fonksiyonu
async function checkPwnedPassword(password) {
const sha1 = await sha1Hash(password);
const prefix = sha1.substring(0, 5);
const suffix = sha1.substring(5).toUpperCase();
const response = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`);
const data = await response.text();
const lines = data.split("\n");
for (const line of lines) {
const [hashSuffix, count] = line.split(":");
if (hashSuffix === suffix) {
return parseInt(count); // şifre sızdırılma sayısı
}
}
return 0;
}
async function sha1Hash(password) {
const encoder = new TextEncoder();
const data = encoder.encode(password);
const hashBuffer = await crypto.subtle.digest('SHA-1', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return hashHex.toUpperCase();
}
</script>
Javascript kodlarını yukarıdan aşağıya sırayla anlatmam gerekirse;
Öncelikle checkPassword() fonksiyonumuzu tanımlayıp, id verdiğimiz div ve li taglerini tek tek tanımlıyoruz.
strength değişkeni, şifrenin puanını tutuyor en başta sıfır olarak belirlememizin sebebi input'un boş olmasıdır. charsetSize ise karakter setinin büyüklüğünü hesaplayıp bu hesaplamayı da bruteforce hesaplamasında değerlendirmek için tanımlıyoruz.
Eğer input alanı boşsa puan, puan barı ve diğer değerler sıfırlanır.
Şifre uzunluğu, büyük harf, küçük harf, rakam, özel karakter, tekrar eden karakter ve artan/azalan karakter sorguları regex yardımıyla inputa girilen veriyi kontrol ederek şifre puanına ekleme yapar.
Güç durumu ve puan parı sorgusunda, alınan puan sayısında bar rengini değiştirmeyi sağlar.
Bruteforce yöntemiyle şifrenin kırılma süresi hesaplanır ve kullanıcıya gösterilir.
pwnedpasswords sitesinin API'sini kullanarak kontrol edilen şifrede sızıntı olup olmadığı kullanıcıya gösterilir.
formatTime fonksiyonu bruteforce süresi hesaplandığında saniye çıktısı verdiğinden insan tarafından okunabilir olması açısında saniyeyi yıla, güne, saate, dakikaya ve saniyeye çevirir.
checkPwnedPassword fonksiyonu API yardımıyla gelen şifreyi önce Sha1 olarak şifreler çünkü pwnedpassword sitesi k-Anonymity denen bir model kullanıyor. Bu modelde şifrenin tamamına göndermek yerine şifrenin Sha-1 hashinin ilk 5 karakterini alıp olası eşleşmeleri sunuyor.


Öncelikle checkPassword() fonksiyonumuzu tanımlayıp, id verdiğimiz div ve li taglerini tek tek tanımlıyoruz.
strength değişkeni, şifrenin puanını tutuyor en başta sıfır olarak belirlememizin sebebi input'un boş olmasıdır. charsetSize ise karakter setinin büyüklüğünü hesaplayıp bu hesaplamayı da bruteforce hesaplamasında değerlendirmek için tanımlıyoruz.
Eğer input alanı boşsa puan, puan barı ve diğer değerler sıfırlanır.
Şifre uzunluğu, büyük harf, küçük harf, rakam, özel karakter, tekrar eden karakter ve artan/azalan karakter sorguları regex yardımıyla inputa girilen veriyi kontrol ederek şifre puanına ekleme yapar.
Güç durumu ve puan parı sorgusunda, alınan puan sayısında bar rengini değiştirmeyi sağlar.
Bruteforce yöntemiyle şifrenin kırılma süresi hesaplanır ve kullanıcıya gösterilir.
pwnedpasswords sitesinin API'sini kullanarak kontrol edilen şifrede sızıntı olup olmadığı kullanıcıya gösterilir.
formatTime fonksiyonu bruteforce süresi hesaplandığında saniye çıktısı verdiğinden insan tarafından okunabilir olması açısında saniyeyi yıla, güne, saate, dakikaya ve saniyeye çevirir.
checkPwnedPassword fonksiyonu API yardımıyla gelen şifreyi önce Sha1 olarak şifreler çünkü pwnedpassword sitesi k-Anonymity denen bir model kullanıyor. Bu modelde şifrenin tamamına göndermek yerine şifrenin Sha-1 hashinin ilk 5 karakterini alıp olası eşleşmeleri sunuyor.







