Laravel veritabanınıza şifrelenmiş değerler yedeklerken ve bunları farklı bir sunucuya geri yüklerken karşılaşabileceğiniz sorunlardan bir tanesi, eski anahtar ile şifrelenmiş verilerin yeni sunucuda okunamamasıdır. Böyle bir durumda yedekleme, teknik olarak tam olsa da pratikte işe yaramaz hale gelir. Aşağıda, bu sorunu nasıl çözebileceğinizi açıklayacağım.
<h2>
<a name="the-real-cause-raw-crypt-endraw-is-bound-to-raw-appkey-endraw-" href="#the-real-cause-raw-crypt-endraw-is-bound-to-raw-appkey-endraw-"></a>
Gerçek neden: <code>Crypt</code>, <code>APP_KEY</code> ile ilişkilidir
</h2>
<p>Duyarlı ayarları (API token’ları veya OAuth gizli anahtarları gibi) veritabanında sakladığınızda, genellikle bunları <code>Crypt::encryptString()</code> ile şifrelersiniz. Ancak unutmayın ki <code>Crypt</code>, uygulamanızın <code>APP_KEY</code>'sini anahtar olarak kullanır.</p>
<p>Naif bir yedekleme, bu şifreli metni olduğu gibi kopyalar:</p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="c1">// Naif yaklaşım — şifreli metni olduğu gibi taşı</span>$value = DB::table(‘settings’)->where(‘key’, ‘some.secret’)->value(‘value’);
// bu değer, ESKİ sunucunun APP_KEY ile şifrelenmiştir.
<p>Yeni sunucuda farklı bir <code>APP_KEY</code> vardır. Şifreyi çözmeye çalıştığınızda → <code>DecryptException: The payload is invalid</code> hatası alırsınız. Yedeklemeniz teknik olarak tamamdır ancak pratikte işe yaramaz.</p>
<h2>
<a name="the-fix-decrypt-on-the-way-out-reencrypt-on-the-way-in" href="#the-fix-decrypt-on-the-way-out-reencrypt-on-the-way-in"></a>
Çözüm: Çıkarken şifreyi çözüp girerken yeniden şifrelemek
</h2>
<p>Alınacak karar oldukça basit: <strong>asla şifreli metni sunucu sınırlarını geçerken taşımayın.</strong> Bunun yerine —</p>
<ol>
<li><strong>Oluşturma</strong>: Değerleri kaynak sunucunun <code>APP_KEY</code> ile şifreyi çözün ve arşiv içine <strong>düz metin</strong> olarak saklayın.</li>
<li>Bu arşivi <strong>AES-256 ve bir şifre</strong> ile koruyun (insanın elinde tuttuğu bir gizli anahtar, APP_KEY değil).</li>
<li><strong>Geri yükleme</strong>: Değerleri hedef sunucunun <code>APP_KEY</code> ile yeniden şifreleyerek veritabanına yazmadan önce işlemi gerçekleştirin.</li>
</ol>
<p>Analojiyi kullanarak: mektubu çözüp, düz mektubu kilitli bir çantada (şifre korumalı arşiv) alıyorsunuz ve yere varınca yeni evin kilidi ile yeniden kodluyorsunuz. Çanta güvenliği taşıma sırasında sağlıyor — artık geçerliliği kalmayan eski kod değil.</p>
<p>Bunu <code>ConfigBackupService</code> içinde açıkça vurguladım:</p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="cd">/ Konfigürasyon Yedekleme & Geri Yükleme.
.env ve DB’de saklanan ayarları tek bir AES-256, şifreli
ZIP dosyası halinde toplar. Arşiv içindeki içerik DÜZ METİN olarak saklanır
böylece şifreli DB kolonları, hedef sunucunun APP_KEY’iyle Geri Yükleme sırasında
yeniden şifrelenir — yedekleme, sunucular arası taşınabilirliği sağlar.
/
class ConfigBackupService { / … */ }“Açık” düz metin arşiv içinde göz korkutucu görünebilir ama güvenlik sınırı isteyerek değişti: APP_KEY’den (her sunucu için farklı olmasını istediğiniz) arşiv şifresine (kontrol ettiğiniz ve döndürebildiğiniz) geçiş yaptı. Bu, bir nesnenin taşınmasının temel işlevi için doğru bir takas.
Yetkilendirme: Tek bir gerçek kaynak, dağınık kontroller değilYine aynı şekilde, yetkilendirme kontrollerini UI, yollar ve komutlar boyunca dağınık şekilde koymak yerine hepsinin referans aldığı bir yöntem kullanarak sağlamlaştırın:
/Geçerli bağlamın yapılandırılmış yetkilendirme kapısını geçip geçmediği.
Kapı yapılandırılmamışsa true döner. CLI komutları sunucu operatörü tarafından
public function authorizes(): bool { $gate = $this->gate();kasıtlı olarak bypass edilir. Yetkilendirme için tek bir gerçek kaynak. */
return $gate === null || Gate::allows($gate); }
İki ince ama önemli nokta:
- Kapı null olabilir. Eğer ana uygulama bir kapı ayarlamıyorsa, paket kendi politikasını dayatmaz — yol ara yazılımlarına güvenebilirsiniz. İyi bir araç önerir, dayatmaz.
- CLI kasıtlı olarak bunu bypass eder.
php artisan config-backup:createkomutunu sunucuda çalıştıran birisi zaten shell erişimine sahiptir. Onları web kapısından zorlamak bir şovdan başka bir şey değildir.
Dürüst kalmasını bir test ile sağlaTaşınabilirlik kısmı “gözle” doğrulaması zor. Bunu bir gidip gelme testi ile dürüst hale getiriyorum: bir anahtar ile şifrele, farklı bir anahtarı simüle et ve geri yüklemenin orijinal değeri okumasını sağla:
it('restore işlemleri farklı APP_KEY altında çalışır', function () { config(['app.key' => 'base64:'.base64_encode(random_bytes(32))]); DB::table()->insert( => , => Crypt::encryptString(), ]);<span class="nv">$backup</span> <span class="o">=</span> <span class="nf">app</span><span class="p">(</span><span class="nc">ConfigBackupService</span><span class="o">::</span><span class="n">class</span><span class="p">)</span><span class="o">-></span><span class="nf">create</span><span class="p">(</span><span class="n">password</span><span class="o">:</span> <span class="s1>'pa55'</span><span class="p">);</span> <span class="c1">// Hedef sunucunun simülasyonu: farklı bir APP_KEY</span> <span class="nf">config</span><span class="p">([</span><span class="s1>'app.key'</span> <span class="o">=></span> <span class="s1>'base64:'</span><span class="mf">.</span><span class="nb">base64_encode</span><span class="p">(</span><span class="nb">random_bytes</span><span class="p">(</span><span class="mi>32</span><span class="p">))]);</span> <span class="no">DB</span><span class="o">::</span><span class="nf">table</span><span class="p">(</span><span class="s1>'settings'</span><span class="p">)</span><span class="o">-></span><span class="nf">truncate</span><span class="p">(</span><span class="p">);</span> <span class="nf">app</span><span class="p">(</span><span class="nc">ConfigBackupService</span><span class="o">::</span><span class="n">class</span><span class="p">)</span><span class="o">-></span><span class="nf">restore</span><span class="p">(</span><span class="nv">$backup</span><span class="p">,</span> <span class="n">password</span><span class="o">:</span> <span class="s1>'pa55'</span><span class="p">);</span> <span class="nv">$value</span> <span class="o">=</span> <span class="no">DB</span><span class="o">::</span><span class="nf">table</span><span class="p">(</span><span class="s1>'settings'</span><span class="p">)</span><span class="o">-></span><span class="nf">where</span><span class="p">(</span><span class="s1>'key'</span><span class="p">,</span> <span class="s1>'some.secret'</span><span class="p">)</span><span class="o">-></span><span class="nf">value</span><span class="p">(</span><span class="s1>'value'</span><span class="p">);</span> <span class="nf">expect</span><span class="p">(</span><span class="nc">Crypt</span><span class="o">::</span><span class="nf">decryptString</span><span class="p">(</span><span class="nv">$value</span><span class="p">))</span><span class="o">-></span><span class="nf">toBe</span><span class="p">(</span><span class="s1>'super-secret'</span><span class="p">);</span>});
Eğer bu, iki farklı APP_KEY boyunca yeşil kalıyorsa, yedeğinizin gerçekten taşınabilir olduğunu bilirsiniz — sadece “benim makinemde çalışıyor” değil.
SonuçBir şeyi sınır aşan olarak tasarladığınızda — sunucu, ortam, kiracı — sorun: bu nesne ile hangi anahtar birleştirilmiş ve diğer tarafta bu anahtar mevcut mu? Cevap genellikle hayırdır, bu nedenle en güvenli şey, kontrol ettiğiniz bir kap içinde düz metin taşımaktır — geride bıraktığınız bir anahtara bağlı şifrelenmiş metin değil.
Paket açık kaynaktır: github.com/cleaniquecoders/laravel-config-backup.
Kaynak: Orijinal Makale


