Veri Şifreleme
Starter kit veritabanındaki küçük bir hassas veri kümesini şifreler. Bu döküman hangi anahtarın bu veriyi koruduğunu, bu anahtarın APP_KEY ile ilişkisini ve adanmış anahtarı benimserken/rotasyon yaparken çalıştırılacak komutları anlatır.
Ne şifreleniyor, hangi anahtarla
| Veri | Nerede | Kullanılan anahtar |
|---|---|---|
Hassas ayar değerleri (mail.password, storage.spaces_secret, storage.aws_secret, storage.hetzner_secret, turnstile.secret_key, postman.api_key, apidog.access_token) |
settings.value |
DataCrypt (adanmış anahtar) |
| İki faktörlü doğrulama sırrı ve kurtarma kodları | users.two_factor_secret, users.two_factor_recovery_codes |
DataCrypt (adanmış anahtar) |
Session'lar, imzalı URL'ler, cookie'ler ve hâlâ Crypt facade'ını çağıran uygulama kodu |
— | APP_KEY (değişmedi) |
DataCrypt (Lvntr\StarterKit\Support\Encryption\DataCrypt), Laravel'in Crypt facade'ıyla aynı API'ye sahip bir facade'dır (encryptString(), decryptString(), encrypt(), decrypt()). Tek fark hangi anahtarın kullanıldığıdır.
APP_KEY vs DATA_ENCRYPTION_KEY
Bunlar bağımsız yaşam döngülerine sahip iki ayrı anahtardır:
APP_KEY— Laravel'in kendi anahtarı. Session'ları, cookie'leri, imzalı URL'leri veCryptfacade'ını destekler.php artisan key:generatebu anahtarı yeniden üretir.DATA_ENCRYPTION_KEY— yukarıda listelenen ayar ve 2FA verisi için adanmış bir anahtar. Kendi.envdeğişkenine ve kendi komutlarına (encryption:key,encryption:rekey,encryption:health) sahiptir.php artisan key:generateDATA_ENCRYPTION_KEYveyaDATA_ENCRYPTION_PREVIOUS_KEYS'i asla okumaz veya yazmaz — yalnızcaAPP_KEY'e dokunur.
Bu ayrım şu nedenle var: key:generate çalıştığı anda her şifreli ayarı ve her kullanıcının 2FA sırrını sessizce bozuyordu (Crypt/Fortify'ın varsayılan encrypter'ı APP_KEY'e bağlı, SettingService de ortaya çıkan DecryptException'ı yutup null döndürüyor, dolayısıyla hata görünmüyordu bile). Adanmış anahtar bu bağımlılığı kaldırır.
Anahtar çözümleme sözleşmesi
Bu tablo bütün güvenlik özelliğidir. DataEncrypterFactory tarafından uygulanır ve DataCrypt tarafından zorlanır.
| Durum | Birincil anahtar (yazma) | Previous-key zinciri (okuma) |
|---|---|---|
DATA_ENCRYPTION_KEY boş (adanmış anahtarı henüz benimsememiş bir kurulum) |
APP_KEY |
DATA_ENCRYPTION_PREVIOUS_KEYS (varsa) |
DATA_ENCRYPTION_KEY dolu |
DATA_ENCRYPTION_KEY |
DATA_ENCRYPTION_PREVIOUS_KEYS, ardından en sonda APP_KEY |
APP_KEYbirincil anahtar değilse her zaman okuma zincirinin sonuna eklenir — benimseme öncesi yazılmış her şey hiçbir komut çalıştırmadan okunabilir kalır.- Zincir sırayla denenir, tekrarlar temizlenir, boş elemanlar atılır.
- Zincirdeki bozuk bir anahtar sessizce atlanmaz —
RuntimeExceptionfırlatılır. Sessiz atlama bir değeri rotasyon ortasında okunamaz gibi gösterip operatörüDATA_ENCRYPTION_PREVIOUS_KEYS'i boşaltmaya iter — bu ise kalıcı veri kaybıdır.
Hiçbir şey yapmamak geçerli bir tercihtir. encryption:key'i hiç çalıştırmayan bir uygulama bugünküyle bit-bit aynı çalışmaya devam eder: DATA_ENCRYPTION_KEY boş kalır, birincil anahtar APP_KEY olmaya devam eder, davranış değişmez. composer update/sk:update sonrası hiçbir şey benimsemeyi zorlamaz.
Üç komut
| Komut | Amaç | Diske yazar mı? |
|---|---|---|
php artisan encryption:key |
Yeni bir DATA_ENCRYPTION_KEY üretir ve yerini aldığı anahtarı DATA_ENCRYPTION_PREVIOUS_KEYS'te korur |
Evet — .env |
php artisan encryption:rekey |
Her ayar/2FA satırını mevcut birincil anahtara yeniden şifreler | Evet — veritabanı (.env değil) |
php artisan encryption:health |
Her satırın hangi anahtara ihtiyacı olduğunu ve DATA_ENCRYPTION_PREVIOUS_KEYS'in temizlenmesinin güvenli olup olmadığını raporlar |
Hayır — salt okunur |
encryption:key
php artisan encryption:key
- Mevcut birincil anahtarı
.env'den çözer (önbelleklenmiş config'ten değil), yapılandırılmış cipher için bellekte rastgele yeni bir anahtar üretir,DATA_ENCRYPTION_PREVIOUS_KEYS'i eski birincili başa ekleyerek yazar ve ancak ondan sonra yeniDATA_ENCRYPTION_KEY'i yazar. Bu sıra kasıtlıdır: iki yazma arasında bir çökme, eski anahtarı hâlâ birincil ve fazladan listelenmiş bırakır — asla tersi değil, tersi her şifreli satırı sahipsiz bırakırdı. .env, uygulamanın önyüklendiği aynı ayrıştırıcıyla (phpdotenv) okunur, regex ile değil —${VAR}ile enterpolasyonlu bir atama (örn.DATA_ENCRYPTION_KEY=${APP_KEY}),DATA_ENCRYPTION_PREVIOUS_KEYS'e yazılmadan önce gerçek anahtar materyaline çözülür, hiçbir zaman${APP_KEY}referansı olarak değil. Ayrıştırıcının anlam veremediği bir.env, hiçbir anahtar üretilmeden veya hiçbir şey yazılmadan önce komutu durdurur — yarım uygulanmış bir rotasyon kalmaz — ve ayrıştırıcının kendi hata mesajı gösterilmez, çünkü bozuk satırı olduğu gibi alıntılayabilir ve bu satır anahtar materyali olabilir.- Yetki dosyada olmalı. Süreç ortamı bu anahtarlardan birini ayarlıyorsa — ya da
.enviçindeki enterpolasyonlu bir değerin işaret ettiği bir değişkeni ayarlıyorsa — çalışan uygulama dosyanın söylediği değeri değil, o değeri çözer.encryption:keybu ayrışmayı tespit edip hiçbir şey üretmeden durur; anahtarın adını söyler, iki değerden hiçbirini yazmaz..env'i yeniden yazmak işe yaramaz: süreçteki değer kazanmaya devam eder ve rotasyon, uygulamanın hiç kullanmadığı bir anahtarı emekliye ayırıp gerçekten kullandığını listeden düşürür — mevcut şifreli veri okunamaz hale gelir. Süreç değişkenini kaldırın (ya da dosyayla aynı değere getirin) ve komutu tekrar çalıştırın. APP_KEYbu komut tarafından hiçbir seçenekte okunmaz, değiştirilmez, yeniden yazılmaz.--showyeni üretilen bir anahtarı stdout'a yazar, hiçbir şey diske yazmaz..env'e dokunmadan bir anahtarın neye benzediğine bakmak için kullanın.- Production gibi görünen bir ortamda çalıştırmak için
--forcegerekir, çünkü anahtarı burada döndürmekencryption:rekeytamamlanana kadar her şifreli değeri okunamaz yapar.--force'u ancak bir veritabanı yedeğiniz ve bir bakım penceresi varken tekrar çalıştırın. - Yeni anahtar
.env'e yazılır ama asla ekrana basılmaz. Yerini aldığı anahtar da asla basılmaz veya loglanmaz — çıktıda yalnızca geldiği değişkenin adı görünür.
encryption:rekey
php artisan encryption:rekey
php artisan encryption:rekey --dry-run
php artisan encryption:rekey --only=settings
php artisan encryption:rekey --only=two-factor
php artisan encryption:rekey --chunk=200
- Birincil olmayan bir anahtarla çözülen her ayar ve 2FA satırını mevcut birincil anahtara yeniden şifreler. Yapılandırılmış hiçbir anahtarın okuyamadığı bir satır dokunulmadan bırakılır ve raporlanır — asla silinmez veya boşaltılmaz.
--dry-runher çözme denemesini yapar ve aynı özeti tek bir bayt yazmadan basar.--only=settingsveya--only=two-factor(birleştirmek için virgülle) çalıştırmayı tek bir yüzeyle sınırlar.--chunk=<n>(varsayılan 200, maksimum 2000) her gidiş-dönüşte kaç satırın okunup kilitlenip yeniden yazılacağını kontrol eder.- Bu komut bir bakım penceresine aittir. Her chunk'ı bir transaction altında yeniden okur ve kilitler, böylece eşzamanlı bir yazma bayat bir yeniden yazmayla ezilmez — ama büyük bir rekey'in yoğun trafikli bir production veritabanında planlı bir pencere dışında çalıştırılmaması gerekir.
- Rekey hiçbir
updated_at'i güncellemez — bu bir depolama formatı değişikliğidir, iş kuralı değişikliği değil.
encryption:health
php artisan encryption:health
php artisan encryption:health --json
Salt okunur — kilit almaz, transaction açmaz, canlı bir veritabanına karşı çalıştırmak güvenlidir.
Sonuçlar (exit kodu makine tarafından okunabilir yarısıdır):
| Sonuç | Exit kodu | Anlamı |
|---|---|---|
safe-to-clear |
0 | Taranan her değer yalnızca birincil anahtarla okunuyor; her yüzey tamamen tarandı. DATA_ENCRYPTION_PREVIOUS_KEYS temizlenebilir. |
rekey-required |
1 | En az bir değer birincil olmayan bir anahtara ihtiyaç duyuyor. Henüz hiçbir şey kaybolmadı, ama previous-key listesini şimdi temizlemek kaybettirir. encryption:rekey çalıştırın. |
not-covered |
1 | Bir yüzey, bu kitin kurmadığı bir encrypter tarafından servis ediliyor ya da starter-kit.encryption config bloğu yok olduğu için yapılandırılan anahtar etkisiz. Hiçbir şey kaybolmadı, ama aşağıdaki atıf kurulumun gerçek okuma/yazma yolunu tarif etmiyor; bu yüzden buradan "safe to clear" iddia edilemez. Bkz. aşağıdaki Yüzey kapsamı. |
incomplete |
1 | Bir yüzey tam olarak taranamadı, dolayısıyla "safe" iddia edilemez — çözdüğü anahtar zinciri artık .env/süreç ortamıyla eşleşmeyen önbelleklenmiş bir config de buna dahildir. php artisan config:clear çalıştırın (production'da config önbelleği kullanıyorsanız ardından yeniden önbelleğe alın) ve tekrar çalıştırın. |
unreadable |
2 | Yapılandırılmış hiçbir anahtarın okuyamadığı bir değer var. Onu yazan anahtar .env'den eksik ve geri eklenmeli — asla temizlenmemeli. |
key-error |
2 | Anahtar zincirinin kendisi çözülmüyor; hiçbir şey atfedilemedi. |
Sonuç yalnızca aşağı düşer, asla yükselmez — yanlış bir "safe to clear" bu komutun veri kaybına yol açabilecek tek çıktısıdır.
Yüzey kapsamı
Her iki komut da yalnızca kitin kendi anahtar zincirini değil, her yüzeyi gerçekte hangi encrypter'ın servis ettiğini raporlar. Bu önemlidir, çünkü 2FA yüzeyi zorunlu olarak kit üzerinden okunmaz: Fortify Fortify::$encrypter ?? Model::$encrypter ?? Crypt sırasını çözer ve kit, tüketicinin kendi ayarladığı bir encrypter'ı bilinçli olarak ezmez (StarterKitServiceProvider::configureDataEncryption()).
encryption:healthher yüzeyin arkasındaki encrypter'ı isimlendirir. Kitin kurmadığı bir encrypter tarafından servis edilen yüzey unvouched (kefil olunamayan) olarak raporlanır ve sonuçnot-covered'a (exit 1) düşer — satır atfı saklanan baytlar hakkında hâlâ doğrudur, ama bu kurulumun okuyup yazdığı yol hakkında hiçbir şey söylemez.- Eski yayınlanmış config boşluğu ayrı bir teşhis olarak raporlanır. Encryption bloğundan önce yayınlanmış bir
config/starter-kit.php,starter-kit.encryptiondeğerini null yapar; böyleceDATA_ENCRYPTION_KEYetkisiz kalır ve birincil anahtar sessizceAPP_KEYyedeğine düşer — bu durum önceden "safe to clear" olarak okunabiliyordu. Artıknot-coveredverir; config'i yeniden yayınlayın (php artisan vendor:publish --tag=starter-kit-config --force) ve tekrar çalıştırın. - Seçilen bir yüzey unvouched ise
encryption:rekeytek bir satır bile okumadan reddeder ve hem yüzeyi hem de onu dışarıda bırakan--only=bayrağını isimlendirir. Sessizce daraltılıp ardından eksiksiz bir rekey gibi raporlanan bir çalıştırma, hatadan daha kötüdür: satırları, okuyan encrypter'ın elinde olmayan bir anahtara yeniden yazar ve her 2FA girişini başarısız bir challenge'a çevirir. Seçilen yüzeylerin tamamı kapsam içindeyse çalıştırma bundan etkilenmez.
Mevcut bir kurulumda adanmış anahtarı benimsemek
encryption:key'i hiç çalıştırmamış bir kurulumun bunu yapması zorunlu değildir. Adanmış anahtarı benimsemeyi seçerseniz şu dört adımı sırayla çalıştırın:
php artisan encryption:key
php artisan config:clear # config önbelleklenmişse rekey HÂLÂ eski birincil anahtarı çözer
php artisan encryption:rekey
php artisan encryption:health
config:clear iki komutun arasında durur, sonrasında değil. encryption:key .env'i yazar ama önbelleklenmiş config önceki zinciri sunmaya devam eder — dolayısıyla temizlemeden önce çalıştırılan bir rekey, her satırı az önce emekliye ayrılan anahtara yeniden şifreler ya da yapacak hiçbir şey bulamaz. Önce temizlemek, encryption:rekey'in veriyi taşıması gereken anahtarı görmesini sağlayan adımdır.
Ardından, yalnızca encryption:health safe-to-clear raporladıktan sonra:
# .env'i elle düzenleyin ve değeri temizleyin:
DATA_ENCRYPTION_PREVIOUS_KEYS=
php artisan config:clear
php artisan encryption:health
Production'da config önbelleği kullanıyorsanız yukarıdaki her config:clear'dan hemen sonra php artisan config:cache'i tekrar çalıştırın.
Düzenlemeden sonraki ikinci encryption:health çalıştırması da safe-to-clear raporlamadan DATA_ENCRYPTION_PREVIOUS_KEYS'i temizlemeyin. Başka bir şey raporlarsa eski değeri geri koyun ve araştırın — her satır birincil anahtarda doğrulanmadan previous-key listesini temizlemek, bir rotasyonu kalıcı veri kaybına çevirir.
Zaten benimsenmiş bir anahtarı rotasyona sokmak
Rotasyon aynı üç komutu, aynı sırayla, aynı bakım penceresi gerekliliğiyle kullanır:
php artisan encryption:key --force # --force yalnızca production gibi bir ortamda gerekli
php artisan config:clear # config önbelleklenmişse rekey HÂLÂ eski birincil anahtarı çözer
php artisan encryption:rekey
php artisan encryption:health
Ardından DATA_ENCRYPTION_PREVIOUS_KEYS'i elle temizleyin, config:clear çalıştırın ve yukarıdaki benimseme akışındaki gibi tekrar encryption:health çalıştırıp doğrulayın. encryption:rekey'i bir bakım penceresi içinde çalıştırın — komut eşzamanlı okuma/yazmaya karşı güvenli yazılmış olsa da büyük bir tabloyu aktif trafik ortasında rekey'lemek doğru değildir.
Bu özelliğin geri alınması (rollback)
Şifreleme kodunun kendisi geri alınırsa (git revert), DataCrypt var olmaktan çıkar ve her çağrı noktası APP_KEY'e bağlı olan Crypt'e düşer. Adanmış anahtarla yazılmış her satır, revert anında okunamaz hale gelir — önce şunu yapmadıkça:
.env'deDATA_ENCRYPTION_KEY'iAPP_KEYile tam olarak aynı değere ayarlayın.- Her satırın o ortak değere yeniden şifrelenmesi için
php artisan encryption:rekeyçalıştırın. - Kod revert'i ancak ondan sonra uygulayın.
Rotasyon yalnızca kısmen uygulandıysa (örn. encryption:key çalıştı ama encryption:rekey bitmedi), DATA_ENCRYPTION_KEY'i önceki değerine geri döndürün ve DATA_ENCRYPTION_PREVIOUS_KEYS'e dokunmayın — zincir sırayla denendiği için veri okunabilir kalır. encryption:health ile doğrulayın.
Ayrıca bakınız
docs/server-migration-runbook.md— kurulu bir uygulamayı şifreli veri kaybetmeden yeni bir sunucuya taşımak için kopyala-yapıştır kontrol listesi.docs/artisan-commands.md— tam komut referansı.