Ders 14 / 15
Parola Saklama
Parolanın karma olarak saklanmasını, farklı tuzla aynı parolanın farklı karma vermesini, maliyet parametresini ve girişte yeniden karmalamayı ölçmek; varsayılan maliyet ortamın elindedir.
Yayın Güncelleme
Bu derste
- password_hash() ve password_verify() ile bir parolayı karmalar ve doğrular, karmanın tamamını basmadan önekini ölçer
- Maliyet parametresinin sonucu kimin belirlediğini taraf ölçümüyle gösterir
- password_needs_rehash() ile eski maliyetli karmayı girişte yeniden karmalar
İçindekiler
İstek Sahteciliği Koruması dersi isteğin kimden geldiğini sunucunun belirlemesini sağladı. Kimliğin kendisi bir parolaya dayanıyorsa, parolanın nasıl saklandığı sorusu kalır: saklanan değeri ve onu üreten ayarı kim belirliyor? Bu ders parolayı saklamanın yerleşik yolunu ve onun bir ayarının ortamın elinde olup olmadığını ölçer.
GÜ5. Bu derste parola ve karma yalnız saklama ve doğrulama için kullanılır. Parola tahmin etmek, karma kırmak ya da saklanmış bir karmayı geri çevirmek modellenmez. Karmanın tamamı hiçbir yerde basılmaz; yalnız önek, uzunluk ve doğrulama sonucu basılır.
Karma ve Doğrulama
Bir parola olduğu gibi saklanmaz. Karma (hash), parolanın geri çevrilemeyen bir özetidir: aynı parola aynı özeti verir, ama özetten parola üretilemez. Parola için bu iş password_hash() ile yapılır. Fonksiyon her çağrıda rastgele bir tuz (salt) üretir ve karmanın içine yazar; böylece aynı parolanın iki karması birbirinden farklıdır. password_verify() parolayı ve karmayı alır, karmanın içindeki tuzu ve ayarı okuyarak doğrular. Karmanın başındaki $2y$12$ öneki algoritmayı (2y, bcrypt) ve maliyet parametresini (cost) taşır.
Aşağıdaki dosya, derste kullanılan fonksiyonları tanımlar: karma üretme, önek alma ve giriş sırasında doğrulayıp gerekirse yeniden karmalama.
<?php // parola.php — karmalama, önek ve girişte yeniden karmalama declare(strict_types=1); function karmala(string $parola, ?int $maliyet = null): string { if ($maliyet === null) { return password_hash($parola, PASSWORD_BCRYPT); } return password_hash($parola, PASSWORD_BCRYPT, ['cost' => $maliyet]); } function onek(string $karma): string { return substr($karma, 0, 7); } function girisYap(string $parola, string $kayitliKarma, int $hedefMaliyet): array { if (!password_verify($parola, $kayitliKarma)) { return ['basarili' => false, 'karma' => $kayitliKarma]; } if (password_needs_rehash($kayitliKarma, PASSWORD_BCRYPT, ['cost' => $hedefMaliyet])) { $kayitliKarma = karmala($parola, $hedefMaliyet); } return ['basarili' => true, 'karma' => $kayitliKarma]; }
İlk ölçüm bir parolayı karmalar; doğru ve yanlış parolayı doğrular; aynı parolayı iki kez karmalayıp karmaların farklı olup olmadığına ve ikisinin de doğrulanıp doğrulanmadığına bakar.
<?php // karma.php — karma, önek ve doğrulama declare(strict_types=1); require 'parola.php'; $karma = karmala('ornek-parola'); $ikinci = karmala('ornek-parola'); echo 'önek ', onek($karma), ' · uzunluk ', strlen($karma), "\n"; echo 'doğru parola ', var_export(password_verify('ornek-parola', $karma), true), "\n"; echo 'yanlış parola ', var_export(password_verify('baska-parola', $karma), true), "\n"; echo 'iki karma ', $karma === $ikinci ? 'aynı' : 'farklı', "\n"; echo 'ikinci karma doğrulanır ', var_export(password_verify('ornek-parola', $ikinci), true), "\n";
önek $2y$12$ · uzunluk 60 doğru parola true yanlış parola false iki karma farklı ikinci karma doğrulanır true
Karma 60 karakterdir ve $2y$12$ ile başlar: bcrypt, maliyet 12. Doğru parola doğrulandı, yanlış parola doğrulanmadı. Aynı parolanın iki karması farklıdır, çünkü her çağrıda yeni bir tuz yazıldı; yine de ikincisi de aynı parolayla doğrulandı. Doğrulama karmaları karşılaştırmaz, parolayı karmanın içindeki tuzla yeniden karmalayıp sonucu karşılaştırır. Karmanın tamamı basılmadı; öneki ve uzunluğu ölçü için yeterlidir.
Taraf: Maliyeti Kim Belirliyor
Maliyet parametresi, bir karmanın üretiminin ne kadar iş gerektirdiğini belirler. Açıkça verilmezse çalışma zamanının varsayılanı kullanılır; bu varsayılan PHP’nin sürümüne bağlıdır (8.4 öncesinde 10, sonrasında 12). Aynı koda başka bir ortamda başka bir önek çıkabilir. Ölçüm için ortamın sapması, varsayılan maliyetin 10 olmasıdır. Karar sırası istemci, üçüncü taraf, ortam; hiçbiri değiştiremediyse sunucu.
<?php // taraf.php — bu sonucu kim değiştirebiliyor? declare(strict_types=1); // Kodun dışındaki taraflar, karar sırasıyla: her istek, her dış çağrı, her dağıtım. const DIS_TARAFLAR = ['istemci', 'üçüncü taraf', 'ortam']; /** Sonucu bir kez sapmasız, sonra her dış tarafın sapmasıyla ayrı ayrı hesaplar. */ function sina(callable $sonuc, array $sapmalar): array { $taban = $sonuc([]); $degisti = []; foreach (DIS_TARAFLAR as $taraf) { if (array_key_exists($taraf, $sapmalar)) { $degisti[$taraf] = $sonuc([$taraf => $sapmalar[$taraf]]) !== $taban; } } return $degisti; } /** Sonucu değiştirebilen ilk dış taraf; hiçbiri değiştiremiyorsa sunucu. */ function taraf(array $degisti): string { foreach (DIS_TARAFLAR as $t) { if ($degisti[$t] ?? false) return $t; } return 'sunucu'; } function tarafRaporu(string $ad, callable $sonuc, array $sapmalar): string { $degisti = sina($sonuc, $sapmalar); $satirlar = []; foreach ($degisti as $t => $d) $satirlar[] = $t . ($d ? ' değiştirdi' : ' değiştiremedi'); $sonTaraf = taraf($degisti); echo "$ad → $sonTaraf\n ", implode(' · ', $satirlar), "\n"; return $sonTaraf; }
Sonuç, üretilen karmanın önekidir. İstemcinin sapması başka bir paroladır.
<?php // maliyet.php — karma önekinin tarafı declare(strict_types=1); require 'taraf.php'; require 'parola.php'; function varsayilanOnek(string $parola, int $maliyet): string { return onek(karmala($parola, $maliyet)); } tarafRaporu( 'varsayılan maliyet', fn(array $s) => varsayilanOnek($s['istemci'] ?? 'ornek-parola', $s['ortam'] ?? PASSWORD_BCRYPT_DEFAULT_COST), ['istemci' => 'baska-parola', 'ortam' => 10] );
varsayılan maliyet → ortam
istemci değiştiremedi · ortam değiştirdi
Önekin sahibi ortamdır. İstemcinin parolası önekin hiçbir karakterini değiştirmedi: parola karmanın içeriğini belirler, ayarını belirlemez. Varsayılan maliyet 12 yerine 10 olunca önek $2y$12$ yerine $2y$10$ oldu. Kodda maliyet yazmadığı için ayarı çalışma zamanının sürümü belirliyor; aynı kod başka bir dağıtımda sessizce başka bir maliyetle karma üretir.
Girişte Yeniden Karmalama
Maliyet zamanla artırılır, ama saklanan karmalar eski maliyetle kalır. password_needs_rehash() bir karmanın hedef ayara uyup uymadığını söyler. Parolanın düz metni yalnızca girişte, kullanıcı onu yazdığında elde olduğu için yeniden karmalama da orada yapılır. Aşağıdaki ölçüm maliyeti 10 olan bir karmayı hedef maliyet 12 ile karşılaştırır, sonra doğru ve yanlış parolayla girişi dener.
<?php // giris.php — girişte yeniden karmalama declare(strict_types=1); require 'parola.php'; $eski = karmala('ornek-parola', 10); echo 'eski önek ', onek($eski), "\n"; echo 'yeniden karmalama gerekir ', var_export(password_needs_rehash($eski, PASSWORD_BCRYPT, ['cost' => 12]), true), "\n"; $dogru = girisYap('ornek-parola', $eski, 12); echo 'doğru parola ', var_export($dogru['basarili'], true), ' · önek ', onek($dogru['karma']), "\n"; $yanlis = girisYap('baska-parola', $eski, 12); echo 'yanlış parola ', var_export($yanlis['basarili'], true), ' · önek ', onek($yanlis['karma']), "\n";
eski önek $2y$10$ yeniden karmalama gerekir true doğru parola true · önek $2y$12$ yanlış parola false · önek $2y$10$
Maliyeti 10 olan karma, hedef 12 için password_needs_rehash() ile true verdi. Doğru parolayla girişte karma yeniden üretildi ve önek $2y$12$ oldu. Yanlış parolayla giriş başarısız oldu ve saklanan karmaya dokunulmadı: önek $2y$10$ olarak kaldı. Yeniden karmalamanın girişte yapılması bir tercih değil zorunluluktur, çünkü düz metin parola başka hiçbir anda elde değildir.
Sınırlayıcı Ölçüm
Maliyet açıkça verilirse varsayılan okunmaz. Kodun karmala($parola, 12) yazması, ortamın varsayılanını sonuçtan çıkarır. Aynı taraf ölçümünü bu kez maliyeti sabitleyerek tekrarlayalım; ortamın sapması (10) aynıdır.
<?php // acik.php — maliyet açık verildiğinde taraf declare(strict_types=1); require 'taraf.php'; require 'parola.php'; function acikOnek(string $parola): string { return onek(karmala($parola, 12)); } tarafRaporu( 'açık maliyet', fn(array $s) => acikOnek($s['istemci'] ?? 'ornek-parola'), ['istemci' => 'baska-parola', 'ortam' => 10] );
açık maliyet → sunucu
istemci değiştiremedi · ortam değiştiremedi
Maliyet kodda 12 olarak yazılınca ortamın varsayılanı sonuca girmez: ortamın sapması (10) önekte bir şey değiştirmedi. Sahip sunucudur. Bu, sunucunun ayar sayısını artırması değil, ortamın artık bir şey belirleyememesidir; maliyeti artırmak için kod değiştirilir, password_needs_rehash() ise eski karmaları girişte yeni ayara taşır.
Özet
- Bu dersin ölçü sınıfı varsayılan maliyette ortam, açık maliyette sunucu: önekin maliyet kısmını ilkinde çalışma zamanının varsayılanı, ikincisinde kodun yazdığı sayı belirledi.
password_hash()her çağrıda yeni bir tuz üretir; aynı parolanın iki karması farklıdır ve ikisi depassword_verify()ile doğrulanır. Karma 60 karakterdir, öneki algoritmayı ve maliyeti taşır.- Farklı bir parola önekin sonucunu değiştirmedi: parola karmanın içeriğini belirler, ayarını belirlemez.
password_needs_rehash()eski maliyetli karmayı yakalar; yeniden karmalama düz metin parolanın elde olduğu tek yerde, doğru girişte yapılır. Yanlış parola karmayı değiştirmez.
Sonraki Adım
Parolanın saklanması bir kimlik doğrulama akışının bir parçasıdır. Parola, oturum, belirteç ve yetki birlikte çalıştığında, isteğin kimliğini kim belirliyor? Kimlik Doğrulama Akışları dersi bunu bütün güvenlik bölümünün bulgularıyla birlikte ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.