Kısaca cevap
Python'da şifre üretirken random modülünü değil, standart kütüphanedeki secrets modülünü kullanın. Tek satırlık güvenli bir şifre için "".join(secrets.choice(alfabe) for _ in range(16)) yeterlidir; bağlantı veya API anahtarı gibi değerler için secrets.token_urlsafe() daha pratiktir. Aşağıdaki tüm kodlar Python 3 ile çalıştırılarak denendi.
Bu sayfada
Python random ile şifre üretmek neden güvensiz?
random modülü simülasyon, oyun ve test verisi için tasarlanmıştır. Arkasındaki algoritma (Mersenne Twister) hızlıdır ama öngörülebilirdir: yeterli sayıda çıktıyı gören biri üretecin iç durumunu hesaplayıp sonraki değerleri tahmin edebilir; bunun için ardışık 624 adet 32 bitlik çıktı yeterlidir. Python belgeleri de bu modülün güvenlik amaçlı kullanılmaması gerektiğini açıkça yazar.
Öngörülebilirliği görmek için aynı başlangıç değeriyle (seed) üreteci iki kez çalıştırmak yeterli:
import random
random.seed(2026)
print([random.randint(0, 9) for _ in range(8)])
random.seed(2026)
print([random.randint(0, 9) for _ in range(8)])
[1, 5, 8, 8, 1, 3, 9, 9]
[1, 5, 8, 8, 1, 3, 9, 9]
İki liste birebir aynıdır. Test yazarken işe yarayan bu özellik, şifre üretirken ciddi bir açıktır. secrets modülü ise işletim sisteminin kriptografik rastgele sayı kaynağını kullanır; başlangıç değeri verilemez ve çıktısı tahmin edilemez. Konunun teorisini kriptografi ve rastgelelik yazısında bulabilirsiniz.
secrets ile random farkı nedir?
İki modül de "rastgele" sayı üretir, ama farklı amaçlar için tasarlanmıştır. Python belgeleri, parola, hesap doğrulama ve güvenlik belirteci gibi işler için random yerine secrets modülünün kullanılmasını önerir; random ise modelleme ve simülasyon için tasarlanmıştır.
| Özellik | random | secrets |
|---|---|---|
| Amaç | Simülasyon, oyun, test verisi | Şifre, token, anahtar, doğrulama kodu |
| Kaynak | Mersenne Twister (sözde rastgele) | İşletim sisteminin kriptografik kaynağı |
| Tohum (seed) verilebilir mi? | Evet; aynı tohum aynı diziyi verir | Hayır |
| Çıktı tahmin edilebilir mi? | Yeterli çıktı görülürse evet | Hayır |
| Karşılık gelen işlevler | random.choice, random.randint, random.shuffle | secrets.choice, secrets.randbelow, secrets.SystemRandom().shuffle |
Kural basit: güvenlikle ilgili her değer için secrets, geri kalan her şey için random.
secrets.choice ile en basit şifre oluşturucu
Harf, rakam ve noktalama işaretlerinden oluşan 94 karakterlik bir havuzdan her karakteri bağımsız olarak seçen fonksiyon:
import secrets
import string
def sifre_uret(uzunluk=16):
alfabe = string.ascii_letters + string.digits + string.punctuation
return "".join(secrets.choice(alfabe) for _ in range(uzunluk))
print(sifre_uret())
print(sifre_uret(24))
Örnek çıktı (her çalıştırmada farklı olur):
z7%qtSR%'~EzA&&&
~\]VH#rUU%#k}n99HfARLt,a
secrets.choice, havuzdaki her karaktere eşit olasılık verir. 94 karakterlik havuzdan 16 karakter seçildiğinde yaklaşık 105 bitlik bir rastgelelik (entropi) elde edilir; bu, deneme yanılmayla tahmin edilemeyecek kadar büyük bir sayıdır. Uzunluğun etkisini şifre kaç karakter olmalı yazısında karşılaştırmalı görebilirsiniz.
string.punctuation tırnak, ters bölü ve < > gibi karakterler de içerir. Bazı siteler bu karakterleri kabul etmez; ayrıca komut satırına veya yapılandırma dosyasına yazarken sorun çıkarabilirler. Gerekirse havuzu kendiniz tanımlayın.
Her karakter türünü garanti eden sürüm
Birçok site "en az bir büyük harf, bir rakam ve bir özel karakter" ister. Tamamen rastgele seçimde bir türün hiç çıkmaması düşük de olsa mümkündür. Aşağıdaki fonksiyon önce her gruptan bir karakter alır, kalanını tüm havuzdan doldurur ve sonra sırayı güvenli biçimde karıştırır:
import secrets
import string
def guclu_sifre(uzunluk=16):
gruplar = [
string.ascii_lowercase,
string.ascii_uppercase,
string.digits,
"!@#$%^&*()-_=+?",
]
if uzunluk < len(gruplar):
raise ValueError("Uzunluk en az %d olmalı" % len(gruplar))
tum = "".join(gruplar)
# Her gruptan en az bir karakter
karakterler = [secrets.choice(g) for g in gruplar]
# Kalan konumlar tüm havuzdan
karakterler += [secrets.choice(tum) for _ in range(uzunluk - len(gruplar))]
# Yerleri güvenli biçimde karıştır
secrets.SystemRandom().shuffle(karakterler)
return "".join(karakterler)
for _ in range(3):
print(guclu_sifre(16))
Örnek çıktı (her çalıştırmada farklı olur):
zz0S^aUIS!y&6sQ)
6*0=4w&agX01B?Nn
#ZI2INkk!T25qYTp
Son adımdaki karıştırma önemlidir. Karıştırmazsanız ilk dört karakter her zaman küçük harf, büyük harf, rakam, özel karakter sırasıyla gelir ve bu kalıp tahmin edilebilir. Karıştırma için random.shuffle yerine, yine işletim sisteminin güvenli kaynağını kullanan secrets.SystemRandom().shuffle tercih edildi.
Bu fonksiyonu 4 karakter uzunluğunda 10.000 kez çalıştırarak denedik; her çıktıda dört türün de bulunduğunu doğruladık.
secrets.token_hex ve secrets.token_urlsafe ile token üretmek
Şifre sıfırlama bağlantısı, oturum anahtarı veya API anahtarı gibi insanların yazmayacağı değerler için karakter havuzuyla uğraşmanıza gerek yoktur:
import secrets
print(secrets.token_urlsafe(16))
print(secrets.token_hex(16))
Örnek çıktı (her çalıştırmada farklı olur):
2IP-lq5uE84TxwYqTdGJXQ
e2005d5f2f9df7676effbadb8b883e73
Parametre, karakter sayısını değil rastgele bayt sayısını belirtir. 16 bayt (128 bit) token_urlsafe ile 22 karakterlik, token_hex ile 32 karakterlik bir metin üretir. token_urlsafe çıktısı yalnızca harf, rakam, - ve _ içerdiği için adres satırında sorun çıkarmaz.
Parametreyi hiç vermezseniz (secrets.token_urlsafe()) modül DEFAULT_ENTROPY değerini kullanır. Bu değer Python 3.14'te 32 bayttır, ama belgeler değerin bakım sürümlerinde bile değişebileceğini belirtir. Belirli bir uzunluğa ihtiyacınız varsa bayt sayısını açıkça yazın. Python belgeleri, 2015 itibarıyla 32 baytlık (256 bit) rastgeleliğin tipik kullanımlarda kaba kuvvet saldırılarına karşı yeterli kabul edildiğini belirtir.
İki gizli değeri karşılaştırırken de == yerine secrets.compare_digest(a, b) kullanın. Bu fonksiyon karşılaştırma süresinden bilgi sızmasını önlemek için tasarlanmıştır.
Modülde işinize yarayabilecek iki fonksiyon daha vardır. secrets.randbelow(n), 0 ile n-1 arasında güvenli bir tamsayı döndürür; örneğin 6 haneli bir doğrulama kodu için f"{secrets.randbelow(10**6):06d}" kullanılabilir. secrets.randbits(k) ise k bitlik rastgele bir tamsayı verir. Her ikisi de random modülündeki benzerlerinin güvenli karşılığıdır; kodunuzda random.randint gördüğünüz güvenlikle ilgili her yeri bunlarla değiştirebilirsiniz.
Kelimelerden parola (passphrase) üretmek
Akılda tutulması gereken bir ana parola için rastgele kelimeler daha kullanışlıdır. Güvenlik, kelimelerin sizin tarafınızdan değil secrets tarafından seçilmesinden ve listenin büyüklüğünden gelir:
import secrets
from pathlib import Path
def parola_ifadesi(kelime_dosyasi, kelime_sayisi=5, ayirici="-"):
kelimeler = [
k.strip() for k in Path(kelime_dosyasi).read_text(encoding="utf-8").splitlines()
if k.strip()
]
if len(kelimeler) < 2000:
raise ValueError("Liste çok kısa; binlerce kelimelik bir liste kullanın.")
return ayirici.join(secrets.choice(kelimeler) for _ in range(kelime_sayisi))
print(parola_ifadesi("kelimeler.txt"))
kelimeler.txt her satırda bir kelime içeren bir dosyadır. Örnek çıktı, kullandığınız listeye göre masa-limon-orman-tren-bulut gibi görünür ve her çalıştırmada değişir.
Listenin uzunluğu belirleyicidir. 7.776 kelimelik bir listeden 5 kelime seçmek yaklaşık 64,6 bit entropi verir; 16 kelimelik bir listeden 5 kelime ise yalnızca 20 bit verir ve hızla tahmin edilebilir. Kodu yazmadan denemek isterseniz sitedeki akılda kalır parola üretici aynı mantıkla Türkçe kelimeler kullanır.
Uzunluk ve karakter havuzu nasıl seçilir?
Rastgele üretilen bir şifrenin gücü iki şeye bağlıdır: havuzdaki karakter sayısı ve şifrenin uzunluğu. Entropi, uzunluk × log2(havuz boyutu) formülüyle hesaplanır. Aşağıdaki değerler Python ile hesaplandı:
| Havuz | Uzunluk | Yaklaşık entropi |
|---|---|---|
| Harf + rakam (62) | 12 | 71,5 bit |
| Harf + rakam (62) | 16 | 95,3 bit |
| Harf + rakam + noktalama (94) | 12 | 78,7 bit |
| Harf + rakam + noktalama (94) | 16 | 104,9 bit |
| Harf + rakam + noktalama (94) | 20 | 131,1 bit |
Tablodan görüldüğü gibi özel karakter eklemek entropiyi artırır ama dört karakter uzatmak çoğu zaman daha büyük fark yaratır. Özel karakter kabul etmeyen sistemlerde yalnızca harf ve rakamla 16–20 karakter kullanmak iyi bir çözümdür.
Hızlıca tek bir şifreye ihtiyacınız varsa betik dosyası oluşturmadan komut satırından da çalıştırabilirsiniz:
python -c "import secrets, string; print(''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(20)))"
Örnek çıktı (her çalıştırmada farklı olur): 2fG3BfVfToaCZULdwbyT. Windows'ta komut python yerine py olabilir.
Dikkat edilmesi gerekenler
- Şifreyi günlüğe yazmayın: Üretilen şifreyi log dosyasına, hata mesajına veya sürüm kontrolüne düşürmeyin.
- Saklarken özetleyin: Kullanıcı şifrelerini düz metin ya da MD5/SHA-256 ile saklamayın; Argon2, bcrypt veya scrypt kullanın. Ayrıntılar şifre saklama yazısında.
- Kendi "karıştırma" yönteminizi icat etmeyin: Saat, işlem numarası veya kullanıcı adından türetilen değerler rastgele değildir.
- Güncel Python kullanın:
secretsPython 3.6 ile geldi; daha eski bir sürüm kullanıyorsanız önce sürümü yükseltin,randommodülüne geri dönmeyin. - Uzunluğu öne alın: Havuzu küçük tutup uzunluğu artırmak, genellikle az sayıda karakterle çok sayıda özel sembol zorlamaktan daha kullanışlıdır.
Aynı yaklaşımın tarayıcı ve Node.js karşılığını JavaScript ile güvenli şifre üretme yazısında bulabilirsiniz; C#, Java ve PHP sürümleri de ayrı yazılarda. Kod yazmadan hemen bir şifreye ihtiyacınız varsa şifre üretici tarayıcınızda çalışır ve ürettiği şifreyi hiçbir yere göndermez.
Sık sorulan sorular
Python'da random ile şifre üretmek neden güvensiz?
random modülü öngörülebilir bir sözde rastgele sayı üreteci kullanır ve Python belgeleri onu güvenlik amaçlı kullanmamanızı açıkça belirtir. Üretecin iç durumu tahmin edilebilir; aynı başlangıç değeriyle her seferinde aynı dizi çıkar. Şifre, token ve anahtar için işletim sisteminin güvenli kaynağını kullanan secrets modülünü tercih edin.
secrets modülü için ek paket kurmak gerekir mi?
Hayır. secrets, Python 3.6'dan beri standart kütüphanenin parçasıdır; pip ile bir şey kurmanız gerekmez. Yalnızca import secrets yazmanız yeterlidir.
secrets.token_urlsafe ile secrets.choice arasındaki fark ne?
secrets.choice sizin belirlediğiniz bir karakter havuzundan seçim yapar; hangi karakterlerin kullanılacağını ve uzunluğu siz kontrol edersiniz. token_urlsafe ise verilen bayt sayısı kadar rastgele veriyi harf, rakam, tire ve alt çizgiden oluşan bir metne çevirir. Şifre sıfırlama bağlantısı veya API anahtarı gibi makineler arası değerlerde token_urlsafe pratiktir.
secrets.token_hex ne işe yarar?
secrets.token_hex(n), n bayt güvenli rastgele veriyi onaltılık (hex) metne çevirir; her bayt iki karakter tuttuğu için token_hex(16) 32 karakterlik bir değer döndürür. Parametre vermezseniz Python makul bir varsayılan uzunluk kullanır. API anahtarı, oturum kimliği veya doğrulama bağlantısı gibi makinelerin okuyacağı değerler için uygundur.
Ürettiğim şifreyi veritabanına nasıl kaydetmeliyim?
Kullanıcı şifrelerini düz metin olarak veya MD5, SHA-256 gibi hızlı özetlerle saklamayın. Argon2, bcrypt veya scrypt gibi tuz kullanan ve bilerek yavaş çalışan bir yöntem seçin. Python'da bunun için argon2-cffi veya bcrypt gibi yaygın paketler bulunur.



