Aplikasi Laravel memegang kunci-kunci yang tidak boleh jatuh ke tangan siapa pun: APP_KEY yang mengenkripsi data, password database, kredensial SMTP, dan API key layanan pihak ketiga. Kesalahan mengelolanya jarang terlihat sampai terjadi insiden — dan saat itu terjadi, satu file .env yang tercommit ke repositori bisa berarti akses penuh ke produksi. Chapter ini membiasakan pemisahan tiga hal yang sering dicampur: kode, konfigurasi, dan secret. Kita verifikasi .gitignore di aplikasi segar, membahas prosedur saat secret telanjur bocor, lalu menguji konsekuensi rotasi APP_KEY secara nyata.

Taksonomi Secret di Aplikasi Laravel #

Tidak semua nilai di .env setara sensitifnya — tabel berikut membantu memprioritaskan mana yang wajib dijaga:

JenisContohKonsekuensi kebocoran
Kunci enkripsiAPP_KEYData terenkripsi bisa dibaca; session dipalsukan
Kredensial databaseDB_PASSWORDBaca/tulis/hapus seluruh data
Kredensial mailMAIL_PASSWORDKirim phishing atas nama domain Anda
API key pihak ketigaPAYMENT_GATEWAY_KEYTransaksi dan penagihan disalahgunakan
Konfigurasi non-rahasiaAPP_NAME, APP_LOCALETidak sensitif — boleh dikomit

Prinsip praktisnya: kode ≠ config ≠ secrets. Kode hidup di Git; konfigurasi non-rahasia boleh ikut; secrets tidak pernah masuk repositori — ia ditempatkan per environment lewat .env atau environment variable.

Memastikan .env Tidak Masuk Git #

Aplikasi baru Laravel sudah menyertakan .env di .gitignore — tapi jangan percaya, buktikan. Perintah git check-ignore -v memberi tahu aturan mana yang menang:

text
=== git check-ignore -v .env ===
.gitignore:3:.env       .env
exit: 0

Aturan .env berada di baris 3 .gitignore milik aplikasi, sehingga Git mengabaikan file tersebut (exit code 0). Bandingkan daftar file untracked saat git status: .env.example tercantum, sedangkan .env tidak ada — templat aman ikut dikomit, file berisi nilai asli tidak. File .env.example inilah kontrak tim: semua variabel yang dibutuhkan aplikasi terdaftar di sana dengan nilai placeholder seperti DB_PASSWORD=YOUR_DATABASE_PASSWORD, tanpa satu pun rahasia asli.

Kalau Secret Telanjur Tercommit: Rotasi #

Menghapus file dari commit terakhir tidak menghapus sejarah — git log -p pada commit lama tetap menampilkan secret, dan setiap clone yang sudah mengambilnya ikut menyimpan salinannya. Urutan prioritasnya:

  1. Rotasi secret segera — anggap sudah dibaca pihak lain; ganti password database, terbitkan API key baru, dan seterusnya.
  2. Bersihkan riwayat Git (git filter-repo atau BFG) setelah rotasi — untuk kebersihan, bukan penyelamatan.
  3. Audit log akses layanan terkait untuk deteksi penyalahgunaan.

Rotasi mudah untuk kredensial eksternal; yang perlu perhatian khusus adalah APP_KEY milik Laravel sendiri.

Rotasi APP_KEY dan Konsekuensinya #

APP_KEY adalah kunci AES-256-GCM yang dipakai Crypt untuk mengenkripsi cookie session dan encrypted cast di Eloquent. Perintah rotasinya satu baris:

text
$ php artisan key:generate

   INFO  Application key set successfully.  

Aman selama aplikasi tidak menyimpan data terenkripsi secara permanen. Namun bila ada kolom dengan encrypted cast, ada harga yang harus dibayar. Kita buat model dengan cast tersebut, isi satu record, lalu periksa nilainya:

php
class PesanRahasia extends Model
{
    protected $table = 'pesan_rahasia';

    protected $fillable = ['isi'];

    protected function casts(): array
    {
        return ['isi' => 'encrypted'];
    }
}
text
=== sebelum rotasi APP_KEY ===
id record                 : 1
isi via model (terdekripsi): Rencana bisnis rahasia tahun depan
nilai mentah di database  : eyJpdiI6InFIS2tmejZwVnNoN0JJRklnVUdzWnc9PSIsInZh…
panjang ciphertext       : 256 karakter

Database menyimpan ciphertext 256 karakter — aman dari pembacaan langsung, tetapi hanya bisa dibuka dengan kunci yang sama. Setelah key:generate dijalankan, kita coba membaca record yang sama:

text
=== sesudah rotasi APP_KEY ===
nilai mentah masih ada   : eyJpdiI6InFIS2tmejZwVnNoN0JJRklnVUdzWnc9PSIsInZh…
membaca isi via model   : GAGAL — The MAC is invalid.
kolom encrypted cast kini TIDAK TERBACA (data hilang tanpa key lama)

DecryptException: The MAC is invalid. — ciphertext masih utuh di database, tetapi kunci barunya tidak cocok. Data tersebut hilang kecuali kita masih menyimpan kunci lama (misalnya untuk proses dekripsi ulang bertahap).

Environment per Stage dan Secret di CI #

Aplikasi melewati beberapa stage — lokal, staging, produksi — dan setiap stage memakai nilai sendiri; .env tidak boleh berpindah antar stage. Untuk deployment, isikan environment variable langsung di server atau penyedia hosting. Tim kecil bisa memakai php artisan env:encrypt untuk mengirim .env terenkripsi antar anggota tim — informasi lengkapnya di dokumentasi configuration .

Di pipeline CI, secret disimpan sebagai encrypted secret di platform (GitHub Actions Secrets, dsb.), lalu diekspos sebagai environment variable ke proses build — kode di-push, CI menyuntikkan secret, pengujian berjalan, dan tidak ada jejak secret di log.

Satu jebakan terakhir yang berhubungan dengan .env: fungsi env() hanya mengembalikan nilai saat konfigurasi belum di-cache. Setelah config:cache berjalan di produksi, panggilan env() di kode aplikasi selalu mengembalikan null — baca variabel lewat config() saja. Perilaku ini sudah kita bedah di Konfigurasi Environment , dan mekanisme enkripsi datanya di Encryption .

Secret yang rapi disimpan melengkapi satu lapisan terakhir: konfigurasi aplikasi itu sendiri. Debug yang menyala di produksi, cookie tanpa flag, header yang kosong — kita matikan semuanya di chapter berikutnya: Secure Configuration .