Bcrypt

Tarayıcıdan çıkmadan bcrypt hash’i üretin ya da bir parolayı bir hash ile sınayın: maliyeti ve $2a$, $2b$ veya $2y$ varyantını seçin, her hash’i parçalayın.

Buraya yazdığınız hiçbir şey bu tarayıcıdan çıkmaz. İş, bu sekmedeki bir Web Worker içinde döner: kendi ağ panelinizi açıp bakın, hesaplama sürerken dışarı tek bir istek gitmiyor.

Hash üret

Bir parola, bir maliyet ve bir varyant. Tuz her çalıştırmada bu tarayıcıdan yeni çekilir, bu yüzden aynı parola her seferinde başka bir hash verir: bu bcrypt’in çalışması, bir arıza değil.

Bir parolayı hash ile karşılaştır

Yapıştırılan hash, parola gerekmeden ve beklemeden hemen parçalarına ayrılır. Eşleşme yanıtı için parolayı ekleyin. Bir hash ne burada ne başka yerde geri çevrilebilir.

Bir bcrypt hash’inin içinde gerçekte ne var

Bir bcrypt hash’i altmış karakterlik tek bir dizedir ve her parçası açıktadır. Onu üreten paroladan başka içinde gizli hiçbir şey yoktur, o parola da orada hiç bulunmaz. Bir hash’i parçalarına ayırmak ne anahtar ne parola ne de herhangi bir hesaplama ister: bu sayfa, siz bir tane yapıştırır yapıştırmaz bunu yapar.

  • Varyant, dizenin başladığı alan: $2b$, $2a$ ya da $2y$. Hash’in kimin kurallarıyla üretildiğini söyler.
  • Maliyet, tam olarak iki basamakla yazılır: 04’ten 31’e. O bir üstür, yani maliyet 12 demek 2^12 — 4.096 — tur anahtar germe demektir ve maliyet 13, maliyet 12’nin on üçte bir fazlası değil, iki katı iştir.
  • Tuz, yirmi iki karakter: on altı rastgele bayt, tuzladıkları yanıtın yanında açıkça saklanır.
  • Özet, son otuz bir karakter: yirmi üç baytlık çıktı. Parolaya bağlı olan tek parça budur ve o hash değildir — hash, dizenin tamamıdır.

Bu karakterlerin çekildiği alfabe bcrypt’in kendisininkidir ve standart Base64 değildir: ./A-Za-z0-9 diye gider, yani Base64’ün sonunda artı ile bölü bulunduğu yerde burada başta bir nokta ile bir bölü bulunur. Bunu sıradan bir Base64 çözücüyle çözmek hata değil yanlış baytlar verir, ki bu bir öğleden sonrayı yitirmenin klasik yoludur.

Yetmiş iki baytlık sınır karakterle değil baytla sayılır

bcrypt bir parolanın en çok yetmiş iki baytını okur ve ondan sonrasını hiç dikkate almaz. Yetmiş iki karakter değil — yetmiş iki bayt UTF-8, yani bunun kaç karakter ettiği bir dilin harflerinin kaç bayt tuttuğuna bağlıdır: işaret taşımayan yirmi altı Latin harfinin her biri bir bayt tutar, başka her harf daha fazla tutar. Türkçede fark küçük değildir: Türkçenin kendi harfleri — ş, ğ, ı, ö, ü, ç ve bunların büyükleri — iki bayt tutar, üstelik sıradan Türkçe bunları bol bol taşır, bu yüzden bir Türkçe parola sınıra yetmiş ikiden hatırı sayılır ölçüde önce varır. Yalnızca bu harflerden kurulu bir parola sınıra otuz altıda varır.

  • ASCII karakter başına bir bayt tutar, bu yüzden İngilizce yazan biri sınıra yetmiş iki karakterde varır ve neredeyse hiç varmaz.
  • İbranice, Arapça, Rusça ve Yunanca harf başına iki bayt tutar, bu yüzden sınır otuz altı karakterde gelir.
  • Japonca, Korece ve Çince üç tutar, bu yüzden sınır yirmi dörtte gelir.
  • Emojilerin çoğu dört tutar ve insanların gerçekten yazdıklarının birçoğu birbirine bağlanmış birkaç kod noktasıdır, bu yüzden bir avuç emoji bütün bütçeyi doldurur.

Parola alanının altındaki bayt sayacı, bunun sonradan keşfedilecek bir şey değil yaklaşırken izlenebilecek bir şey olması için oradadır. Kesme de karakterden değil bayttan olur, tam olarak her referans uygulamasının yaptığı gibi — yani sınırın üstüne oturan çok baytlı bir karakter bir parçasını yitirir, kalanını tutar. Bu bir arıza değil uyumlu davranıştır ve bu sayfa olduğunda bunu söyler.

Şimdi geri kalan her şeyi açıklayan kısım: kısaltma yanıtta görünmez. Anahtar çizelgesi durumuna tam olarak yetmiş iki bayt anahtar karıştırır ve daha uzun bir anahtarı asla başa sarmaz, bu yüzden yüz baytlık bir parolanın hash’i ile onun ilk yetmiş iki baytının hash’i aynı özeti verir. Bir uygulamanın saptayacağı hiçbir şey ve bildireceği hiçbir şey yoktur; hiçbir kütüphanenin hata vermemesinin nedeni budur ve ilk yetmiş iki baytında uyuşan iki ayrı uzun parolanın ikisinin birden aynı saklı hash ile eşleşmesinin nedeni de budur. Bu sayfa da uzun bir parolayı geri çeviremez — gerçek bir sistemin kısaltarak ürettiği bir hash ile karşılaştırıyorsanız yanıta ihtiyacınız vardır — bu yüzden yanıtı verir ve neyin kesildiğini söyler.

Varyant ön eki değiştirir, özeti değil

Varyant bir sürüm numarasına benzer, ama değildir. Bu aracın yazdığı üçü sıralı değildir, hiçbiri ötekinin yerini almaz ve aralarında seçim yapmak bir güvenlik kararı değil bir uyumluluk kararıdır.

  • $2b$ OpenBSD’nin karar kıldığıdır ve bugün Python, Node ve Go’nun ürettiğidir. Burada varsayılan odur ve başka bir şey zorlamıyorsa doğru yanıt odur.
  • $2a$ daha eskisidir ve uzun ömürlü kimi Java ile Spring Security kurulumları hâlâ onu görmeyi bekler.
  • $2y$ PHP’nin password_hash işlevinin, dolayısıyla Laravel’in yazdığıdır, bu yüzden o dünyaya yapıştırılan bir hash genelde onu ister.

Üçünü ayıran şey, 255 bayttan uzun bir anahtara nasıl davrandıklarıdır ve bcrypt yetmiş ikide okumayı çoktan bırakmıştır — yani buraya yazabileceğiniz hiçbir parola bu farka erişemez. Bu aracın alacağı her şey için üçü aynı yirmi üç baytı üretir ve yalnızca baştaki dört karakterde ayrılır. Varyantı değiştirin, özet yerinden kıpırdamaz.

İki tane daha vardır ve bu araç onları hesaplamak yerine adıyla anar. $2x$ bir düzeltme değildir: Openwall onu bir işaret genişletme hatasını bilerek yeniden üretmek için türetti, böylece bozuk kodun ürettiği hash’ler yine de karşılaştırılabilsin diye; onu burada uygulamak, bilinen bir kusuru aracın içine koymak olurdu. $2$ ise ilk olanıdır, anahtara sondaki sıfır baytı eklenmeden önceki. İkisini de güncel hiçbir şey üretmez; elinizde bir tane varsa, varyantın bulduğunuz şeylerin en küçüğü olduğu kadar eski bir sistemden çıkmıştır.

Maliyet ve kendi makinenizde neye mal olduğu

Maliyet, 4’ten 31’e giden iş katsayısıdır ve bir üstür: her basamak süreyi ikiye katlar. bcrypt’in bütün amacı budur. Bir parola hash’inin yavaş olması gerekir, çünkü çalınmış bir tablo elinde tutan saldırgan her tahmin için aynı bedeli öder ve maliyeti iki katına çıkarmak, parasının ona saniyede kaç tahmin aldığını yarıya indirir.

Belirli bir maliyetin gerçekte ne kadar sürdüğü, önceden yazılabilecek bir sayı değil onu çalıştıran makine hakkında bir olgudur — bir telefon ile bir sunucu arasında iki büyüklük mertebesi vardır. Bu yüzden bu sayfa alıntılamak yerine ölçer: sayfa yüklenirken kendi cihazınızda kısa bir hash çalıştırır ve oradan yola çıkarak öngörür; maliyet denetiminin yanındaki tahminin bir an boş kalıp sonra belirmesinin nedeni budur. Birkaç saniyenin üstünde, başlamadan önce sorar, böylece yanlış yazılmış bir basamak donmuş bir sayfa gibi okunmaz.

Bir sayı seçmek için: 12, Laravel’in ve Python’un bcrypt paketinin varsayılan olarak ürettiğidir ve bugün üretim için makul bir alt sınırdır. Daha düşük maliyetler test verileri içindir; elli kullanıcı üreten bir takım, kullanıcı başına bir saniye harcamamalıdır. 15’in üstündeki maliyetleri yayımlamadan önce gerçek oturum açma trafiğinize karşı ölçmekte yarar var, çünkü her oturum açma da aynı bedeli öder.

# Apache: write an htpasswd line at cost 12
htpasswd -nbBC 12 alice "correct horse battery staple"

# PHP and Laravel: password_hash emits $2y$
php -r 'echo password_hash("correct horse battery staple", PASSWORD_BCRYPT);'

# Python: bcrypt emits $2b$, and gensalt takes the cost
python -c "import bcrypt; print(bcrypt.hashpw(b'correct horse battery staple', bcrypt.gensalt(12)))"

Tuz ve burada üretilen bir hash’in ne zaman saklanmaya uygun olmadığı

Tuz, her hash için yeniden çekilen on altı rastgele bayttır ve dizenin içinde açıkça saklanır. Bir sır değildir ve hiçbir zaman öyle olması amaçlanmadı: işi, aynı iki parolanın birbiriyle ilgisiz iki hash üretmesini sağlamaktır, böylece çalınmış bir tabloya önceden hesaplanmış tek bir sözlükle saldırılamaz.

Aynı parolanın burada her düğmeye bastığınızda size başka bir hash vermesinin nedeni de budur. Bu bir kusur değildir ve önceki yanıt bayatlamamıştır: yeni bir tuz, bcrypt’in çalışmasıdır. O hash’lerin her biri parolayla eşleşir, çünkü tuz, karşılaştırılan dizenin içinde yolculuk eder.

Gelişmiş tuz alanı tek bir iş için vardır: başkasının hash’ini birebir yeniden üretmek, böylece iki uygulama karşılaştırılabilir. Oraya tam bir hash yapıştırın, maliyet ile varyant kendiliğinden ona uyar ve doğru parola girdiyi karakteri karakterine yeniden üretir. Bu yolla ürettiğiniz her şey karşılaştırmak içindir, saklamak için değil — iki kez kullanılan bir tuz, işini yapmayı bırakmış bir tuzdur.

Bir bcrypt hash’i geri çevrilemez

İnsanlar bunun gibi sayfalara bir bcrypt hash’ini deşifre etmenin yolunu arayarak gelir. Böyle bir yol yoktur ve nedeni zor olması değildir: parola dizenin içinde değildir. Altmış karakter bir varyant, bir maliyet, on altı rastgele bayt ve yirmi üç baytlık çıktı taşır ve bunların hiçbir dizilişi herhangi bir uzunlukta bir parola içermez. Şifrelenen bir şey olmadı, dolayısıyla deşifre edilecek bir şey de yok.

Bir sitenin yapabileceği — ve deşifre reklamı yapanların yaptığı — şey tahmin etmektir. Yaygın parolalardan bir liste alınır, her biri sizin tuzunuza karşı ve sizin maliyetinizde hash’lenir ve herhangi birinin eşleşip eşleşmediğine bakılır. bcrypt tam da bunu pahalı kılmak için tasarlanmıştır, maliyetin varlık nedeni budur ve bu araç bunu hiçbir biçimde sunmaz.

O sorunun dürüst hâli genelde başka bir sorudur ve onun bir yanıtı vardır: bu dizenin içinde ne var, ve tam olarak bu parola onu üretiyor mu? İkisi de bu sayfadadır. İçinde taşıdığı her şeyi görmek için hash’i yapıştırın, evet ya da hayır yanıtı için bir parola ekleyin.

Sıkça sorulan sorular

Parolam bir sunucuya gönderiliyor mu?
Hayır. Her şey bu tarayıcı sekmesinde olur — hash’leme kendi makinenizdeki bir Web Worker içinde çalışır, parola hiçbir zaman URL’ye girmez ve tarayıcı deposuna hiçbir şey yazılmaz. Ağ panelinizi açın, bir parola yazın ve düğmeye basın: dışarı tek bir istek gitmez. Bu sayfa ile aynı soruda onun üstünde çıkan iki site arasındaki fark budur.
Bir bcrypt hash’i parolaya geri deşifre edilebilir mi?
Hayır, ve zor olduğu için değil. Parola dizenin içinde değildir: orada bir varyant, bir maliyet, bir tuz ve yirmi üç baytlık çıktı vardır ve bunların hiçbiri girdiyi içermez. Bir tanesini deşifre etmeyi öneren her şey, sizin tuzunuza karşı yaygın parolaları tahmin ediyordur; bcrypt de özellikle bunu yavaşlatmak için tasarlanmıştır.
Aynı parola neden her seferinde başka bir hash veriyor?
Çünkü her çalıştırmada yeni bir rastgele tuz çekilir ve tuz dizenin bir parçasıdır. O hash’lerin hepsi aynı parolayla eşleşir — karşılaştırıcı, tuzu yapıştırdığınız hash’ten okur, bu yüzden onu hangi çalıştırmanın ürettiğini bilmesi gerekmez.
Parolam yetmiş iki baytta kesildi. Neden hata verilmedi?
Çünkü saptanacak bir şey yok. bcrypt durumuna tam olarak yetmiş iki bayt anahtar karıştırır ve daha ötesini hiç okumaz, bu yüzden uzun bir parola ile onun ilk yetmiş iki baytı aynı özeti üretir — kısaltma, bir kütüphanenin fark edebileceği hiçbir iz bırakmaz. Bu sayfa olduğunu size söyler, ki bir uygulamanın dürüstçe yapabileceği en fazla şey budur.
Parolam neden otuz altı karakterde sınıra dayandı?
Çünkü sınır yetmiş iki karakter değil yetmiş iki bayttır ve İbranice, Arapça, Rusça ile Yunanca harfleri UTF-8’de iki bayt tutar. Japonca, Korece ve Çince üç tutar, orada sınır yirmi dört karakterde gelir, emojilerin çoğu da dört tutar. Türkçenin kendi harfleri — ş, ğ, ı, ö, ü, ç ve bunların büyükleri — de iki bayt tutar, üstelik sıradan Türkçe bunları bol bol taşır: karışık bir Türkçe parola sınıra yetmiş ikiden epeyce önce varır, yalnızca bu harflerden kurulu bir parola ise otuz altıda kesilir. Alanın altındaki sayaç, siz yazdıkça bayt sayısını gösterir.
Hangi varyantı seçmeliyim?
Yapıştıracağınız sistem hangisini bekliyorsa onu: PHP ile Laravel için $2y$, eski Spring Security için $2a$, geri kalan her yerde $2b$. Bu bir güvenlik kararı değil bir uyumluluk kararıdır — bu araca verilebilecek her uzunlukta üçü aynı özetleri üretir ve yalnızca baştaki o dört karakterde ayrılır.
Hangi maliyeti kullanmalıyım?
12 yaygın varsayılandır ve üretim için makul bir alt sınırdır. Test verileri için düşük bir maliyet kullanın, böylece takımınız anahtar germenin bedelini elli kez ödemez; 15’in üstündeki her şeyi önce gerçek oturum açma trafiğinize karşı ölçün, çünkü başarılı her oturum açma da bir saldırganın ödediği bedeli öder.
Burada üretilen bir hash gerçek bir kullanıcı tablosuna konabilir mi?
Bir veritabanını doldurmak, bir test verisi yazmak ya da bir htpasswd satırı eklemek için evet — tuz, tarayıcınızın kriptografik rastgelelik kaynağından gelir. Ama burası bir kimlik bilgisi hizmeti değil bir karalama sayfasıdır: tuzu kendiniz yazarsanız sonuç saklanmak için güvenli olmaktan çıkar, ki sayfa bunu o anda söyler, ve korunmaya değer bir parola nerede yaşayacaksa orada üretilse daha iyidir.
Onu üreten kod bcrypt diyorsa hash’im neden $2y$ ile başlıyor?
Çünkü PHP’nin password_hash işlevi $2y$ yazar ve Laravel onun üstüne kuruludur. Bu, $2b$ ile aynı algoritmadır; varyant, dizeyi hangi uygulamanın yazdığını kaydeder. Onu geri çeviren bir karşılaştırıcı, parolayla eşleştirmekte başarısız olmuyor, varyantı geri çeviriyordur.
Bir hash yapıştırdım ve tuzun başka türlü yazıldığını söylüyor. Bozuk mu?
Hayır. Yirmi iki karakter, on altı baytın gerektirdiğinden daha çok bit kodlayabilir, bu yüzden sondaki dört bit düşürülür ve kimi tuzların aynı baytlara çözülen birden çok yazımı olur. Gerçek sistemler böylelerini üretti, bu yüzden bu sayfa böyle bir hash’i geri çevirmek yerine okur ve size kanonik yazımı söyler.

İlgili araçlar