Kısaca cevap
Python bcrypt ile şifre hashlemek için pip install bcrypt ile paketi kurup bcrypt.hashpw(sifre.encode("utf-8"), bcrypt.gensalt()) çağırırsınız; doğrulama bcrypt.checkpw ile yapılır. Yeni projelerde OWASP'ın ilk tercihi olan Argon2id için argon2-cffi paketindeki PasswordHasher daha iyi bir seçimdir. Paket kuramıyorsanız standart kütüphanedeki hashlib.scrypt de işinizi görür. hashlib.md5 veya hashlib.sha256 şifre saklamak için kullanılmaz.
Bu sayfada
Bu yazıdaki bcrypt ve argon2-cffi örnekleri paketlerin resmi belgelerindeki kullanım biçimine dayanır (bcrypt 5.0.0, argon2-cffi 25.1.0). Standart kütüphaneyle yazılan scrypt örneği Python 3.14 ile çalıştırıldı. Algoritmaların nasıl çalıştığını ve neden yavaş olmaları gerektiğini şifreler nasıl saklanmalı yazısında anlatıyoruz; burada yalnızca Python tarafına odaklanıyoruz.
Python bcrypt hash password: hashpw ve checkpw
Önce paketi kurun:
pip install bcrypt
bcrypt paketi bayt dizileriyle çalışır; metni encode("utf-8") ile bayta çevirmeniz gerekir. gensalt() her çağrıda yeni ve rastgele bir tuz üretir, hashpw da tuzu ve maliyeti özetin içine yazar:
import bcrypt
def sifre_hashle(sifre: str) -> str:
ozet = bcrypt.hashpw(sifre.encode("utf-8"), bcrypt.gensalt())
return ozet.decode("ascii") # veritabanına metin olarak yazmak için
def sifre_dogrula(sifre: str, kayitli: str) -> bool:
return bcrypt.checkpw(sifre.encode("utf-8"), kayitli.encode("ascii"))
kayit = sifre_hashle("Kedi-Bahçe-Mavi-42")
print(kayit) # $2b$12$... ile başlayan 60 karakter
print(sifre_dogrula("Kedi-Bahçe-Mavi-42", kayit)) # True
print(sifre_dogrula("kedi-bahçe-mavi-42", kayit)) # False
Tuzu ayrıca saklamanıza gerek yoktur: $2b$12$ ön ekinden sonra gelen 22 karakter tuzdur ve checkpw onu kayıttan okur. Aynı şifreyi iki kez hashlerseniz, tuz farklı olduğu için iki farklı sonuç alırsınız; bu normaldir. Karşılaştırmayı == ile değil, her zaman checkpw ile yapın.
Maliyet (rounds) nasıl ayarlanır?
gensalt() varsayılan olarak 12 tur kullanır. Maliyeti artırmak için sayıyı parametre olarak verirsiniz:
ozet = bcrypt.hashpw(sifre_bayt, bcrypt.gensalt(rounds=14))
Maliyet logaritmiktir: bir artırmak süreyi yaklaşık iki katına çıkarır. OWASP en az 10 ister ve bir özetin hesaplanmasının bir saniyeden kısa sürmesini önerir. Değeri kendi sunucunuzda time.perf_counter() ile ölçerek seçin.
72 bayt sınırı ve ValueError
bcrypt şifrenin yalnızca ilk 72 baytını kullanır. Paketin 5.0.0 sürümündeki değişiklik notuna göre hashpw, 72 bayttan uzun bir şifre verildiğinde artık ValueError fırlatıyor; önceki sürümler fazlasını sessizce kesiyordu. Dikkat: sınır karakter değil bayt sınırıdır. "ğ", "ş" gibi Türkçe harfler UTF-8'de 2 bayt tutar; 37 tane "Ğ" 74 bayt eder.
İki seçeneğiniz var: şifre uzunluğunu kayıt formunda 72 baytla sınırlamak ya da OWASP'ın önerdiği ön işlemi uygulamak. OWASP, düz SHA-512 ile ön hashlemeyi tehlikeli bulur ve pepper'lı HMAC-SHA384 çıktısının Base64 hâlini önerir:
import base64
import hashlib
import hmac
PEPPER = b"ortam-degiskeninden-okunan-gizli-anahtar" # veritabanında saklamayın
def bcrypt_girdisi(sifre: str) -> bytes:
ozet = hmac.new(PEPPER, sifre.encode("utf-8"), hashlib.sha384).digest()
return base64.b64encode(ozet) # 48 bayt -> 64 karakter, NUL bayt yok
girdi = bcrypt_girdisi("çok uzun bir parola " * 10)
print(len(girdi), b"\x00" in girdi)
64 False
Bu fonksiyonun çıktısını hashpw ve checkpw'ye şifre yerine verirsiniz. Pepper'ı kaybederseniz hiçbir şifre doğrulanamaz; güvenli bir yerde yedekleyin. Yeni bir projede bu karmaşıklığa hiç girmemek için Argon2id kullanmak daha kolaydır.
Python argon2: argon2-cffi ile PasswordHasher
pip install argon2-cffi
Paket argon2-cffi adıyla kurulur ama argon2 adıyla içe aktarılır. PasswordHasher metinle çalışır, tuzu kendisi üretir ve varsayılan olarak Argon2id kullanır:
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
ph = PasswordHasher()
kayit = ph.hash("Kedi-Bahçe-Mavi-42")
print(kayit) # $argon2id$v=19$m=65536,t=3,p=4$... biçiminde
def sifre_dogrula(sifre: str, kayit: str) -> bool:
try:
return ph.verify(kayit, sifre) # önce özet, sonra şifre
except VerifyMismatchError:
return False
print(sifre_dogrula("Kedi-Bahçe-Mavi-42", kayit)) # True
print(sifre_dogrula("yanlis", kayit)) # False
Dikkat edilecek iki nokta: verify yanlış şifrede False döndürmez, VerifyMismatchError fırlatır. Parametre sırası da verify(özet, şifre) şeklindedir. Özet bozuksa InvalidHashError fırlatılır; bunu da yakalamak isteyebilirsiniz.
Varsayılan ayarlar ve OWASP ayarları
argon2-cffi belgesine göre PasswordHasher varsayılanları time_cost=3, memory_cost=65536 (KiB, yani 64 MiB), parallelism=4, hash_len=32, salt_len=16 şeklindedir. Bu, RFC 9106'nın belleği kısıtlı ortamlar için verdiği ikinci öneriyle (RFC_9106_LOW_MEMORY profili) aynıdır. Belge, 64 MB'lık bellek ihtiyacının küçük Docker kapsayıcılarında sorun çıkarabileceği uyarısını da yapar.
Daha az bellekle çalışmanız gerekiyorsa OWASP'ın önerdiği ayarlardan birini seçebilirsiniz, örneğin 19 MiB bellek ve 2 geçiş:
ph = PasswordHasher(time_cost=2, memory_cost=19456, parallelism=1)
OWASP'ın bütün Argon2id ve scrypt ayarlarının tablosu şifre saklama yazısında.
check_needs_rehash ile ayarları güncelleme
Ayarları değiştirdiğinizde eski kullanıcıların özetleri eski ayarlarla kalır. Kullanıcı doğru şifreyle giriş yaptığında, şifre elinizdeyken özeti yenileyebilirsiniz:
def giris(kullanici, sifre: str) -> bool:
try:
ph.verify(kullanici.sifre_ozeti, sifre)
except VerifyMismatchError:
return False
if ph.check_needs_rehash(kullanici.sifre_ozeti):
kullanici.sifre_ozeti = ph.hash(sifre)
kullanici.kaydet()
return True
Aynı yöntemle bcrypt veya MD5 ile saklanmış eski kayıtları da taşıyabilirsiniz: eski yöntemle doğrulayın, başarılıysa Argon2id ile yeniden hashleyip kaydedin.
Python hashlib scrypt example: paket kurmadan
Standart kütüphanedeki hashlib.scrypt (Python 3.6+) tuzlu ve bellek zorlayıcı bir özet üretir. Aşağıdaki sürüm, parametreleri ve tuzu tek bir metinde sakladığı için ayarları ileride değiştirseniz bile eski kayıtları doğrulayabilir:
import base64
import hashlib
import hmac
import os
# OWASP'ın scrypt için verdiği ilk yapılandırma: N=2^17, r=8, p=1 (128 MiB)
N, R, P = 2**17, 8, 1
MAXMEM = 256 * 1024 * 1024 # 128 MiB'lik iş için sınırı yükseltin
DKLEN = 32
def sifre_hashle(sifre: str) -> str:
tuz = os.urandom(16)
anahtar = hashlib.scrypt(sifre.encode("utf-8"), salt=tuz,
n=N, r=R, p=P, maxmem=MAXMEM, dklen=DKLEN)
b64 = lambda b: base64.b64encode(b).decode("ascii")
return f"scrypt${N}${R}${P}${b64(tuz)}${b64(anahtar)}"
def sifre_dogrula(sifre: str, kayit: str) -> bool:
_, n, r, p, tuz_b64, anahtar_b64 = kayit.split("$")
tuz = base64.b64decode(tuz_b64)
beklenen = base64.b64decode(anahtar_b64)
hesaplanan = hashlib.scrypt(sifre.encode("utf-8"), salt=tuz,
n=int(n), r=int(r), p=int(p),
maxmem=MAXMEM, dklen=len(beklenen))
return hmac.compare_digest(hesaplanan, beklenen)
kayit = sifre_hashle("Kedi-Bahçe-Mavi-42")
print(kayit)
print(sifre_dogrula("Kedi-Bahçe-Mavi-42", kayit))
print(sifre_dogrula("kedi-bahçe-mavi-42", kayit))
scrypt$131072$8$1$ig6D/dgLMNI86zRm2ozEFw==$dxuXMiqlZpB1TUJFuygjgXVpOOuQZsoMgM8JyFqTLpk=
True
False
Tuz her çalıştırmada değiştiği için sizin çıktınızın ilk satırı farklı olacaktır. maxmem verilmezse OpenSSL'in varsayılan 32 MiB sınırı yüzünden ValueError: [digital envelope routines] memory limit exceeded hatası alınır; denemede bu hatayı gördük. Buradaki kayıt biçimi bu örneğe özeldir, bir standart değildir. Tuz, parametre ve çıktı alanlarını ayrı sütunlarda da saklayabilirsiniz. Tuzsuz ve hızlı hashlib özetlerinin neden yetersiz kaldığını Python ile MD5 ve SHA-256 yazısında gösteriyoruz.
Python argon2 vs bcrypt: hangisi?
| argon2-cffi (Argon2id) | bcrypt | hashlib.scrypt | |
|---|---|---|---|
| Kurulum | pip install argon2-cffi | pip install bcrypt | Standart kütüphane |
| Girdi türü | str veya bytes | bytes | bytes |
| Tuz | Otomatik | Otomatik (gensalt) | Elle (os.urandom) |
| Şifre uzunluğu | Pratikte sınırsız | En fazla 72 bayt | Belge makul bir üst sınır (ör. 1024) önerir |
| Yeniden hash kontrolü | check_needs_rehash | Yok, maliyeti kendiniz okursunuz | Yok |
| OWASP sırası | 1 | Eski sistemler | 2 |
Dikkat edilmesi gerekenler
- Şifreyi hashlemek için
hashlib.sha256veyamd5kullanmayın; tuz eklemek onları yeterince yavaşlatmaz. - Tuzu
randommodülüyle üretmeyin;os.urandomveyasecretskullanın. Güvenli rastgelelik için Python ile güvenli rastgele şifre üretme yazısına bakın. - Özet sütununu dar tutmayın; Argon2 metni bcrypt'in 60 karakterinden uzundur.
- Şifreleri günlük kayıtlarına (log) veya hata mesajlarına yazmayın.
- Kullanıcıya "bu e-posta kayıtlı değil" ve "şifre yanlış" gibi ayrı mesajlar göstermeyin; tek bir genel hata mesajı kullanın.
Aynı konunun diğer dillerdeki karşılıkları: PHP password_hash kullanımı ve C# ile tuzlu PBKDF2.
Sık sorulan sorular
Python'da bcrypt ile şifre nasıl hashlenir?
pip install bcrypt ile paketi kurun, şifreyi encode ile bayta çevirin ve bcrypt.hashpw(sifre_bayt, bcrypt.gensalt()) çağırın. Dönen değer tuzu ve maliyeti de içeren bayt dizisidir; bunu veritabanında saklarsınız. Doğrularken bcrypt.checkpw(girilen_bayt, kayitli_ozet) True veya False döndürür.
Python argon2-cffi nasıl kullanılır?
pip install argon2-cffi ile kurun, from argon2 import PasswordHasher ile bir PasswordHasher nesnesi oluşturun. hash metodu tuzlu Argon2id özetini metin olarak döndürür; verify(ozet, sifre) eşleşirse True döndürür, eşleşmezse VerifyMismatchError fırlatır. check_needs_rehash ile eski ayarlarla oluşturulmuş özetleri tespit edersiniz.
Python'da argon2 mi bcrypt mi kullanmalıyım?
Yeni bir projede Argon2id'yi (argon2-cffi) tercih edin; OWASP şifre saklamada ilk sıraya Argon2id'yi koyar ve bcrypt'i Argon2 ile scrypt kullanılamayan eski sistemler için önerir. bcrypt de güvenlidir ama 72 baytlık girdi sınırı vardır. Çalışan bir bcrypt sistemini kullanıcılar giriş yaptıkça Argon2id'ye taşıyabilirsiniz.
Paket kurmadan Python'da şifre hashlenebilir mi?
Evet. Standart kütüphanedeki hashlib.scrypt fonksiyonu tuzlu ve bellek zorlayıcı bir özet üretir. OWASP'ın scrypt ayarlarından biri olan N=2^17, r=8, p=1 için maxmem parametresini yükseltmeniz gerekir; aksi halde OpenSSL'in varsayılan bellek sınırı yüzünden ValueError alırsınız.
bcrypt'te 72 karakterden uzun şifre ne olur?
bcrypt yalnızca ilk 72 baytı kullanır. Python bcrypt 5.0.0 ile 72 bayttan uzun şifrede hashpw ValueError fırlatır; eski sürümler fazlasını sessizce keserdi. Türkçe karakterler UTF-8'de 2 bayt tuttuğu için sınır karakter sayısından önce dolabilir. Uzun şifreleri kabul etmek istiyorsanız Argon2id kullanın veya OWASP'ın önerdiği pepper'lı HMAC ön işlemini uygulayın.



