Masalah konfigurasi biasanya muncul pelan-pelan. Awalnya koneksi database ditulis langsung di satu file PHP. Lalu API key ditambahkan ke file lain. Setelah itu, alamat layanan, mode debug, dan password admin ikut tersebar di dalam kode. Aplikasi memang tetap berjalan, tetapi setiap perpindahan server atau perubahan credential menjadi pekerjaan yang rawan salah.
Konfigurasi yang sehat membuat kode aplikasi tidak perlu tahu apakah ia berjalan di laptop developer, server staging, atau production. Kode membaca nilai yang disediakan oleh lingkungan tempat aplikasi berjalan. Dalam PHP, salah satu cara umum untuk mengambil nilai tersebut adalah fungsi getenv(), yang mengembalikan nilai environment variable atau false jika variabelnya tidak tersedia.
Bedakan setting biasa dan secret
Tidak semua konfigurasi memiliki tingkat risiko yang sama. Nama aplikasi, zona waktu, atau mode tampilan mungkin bukan rahasia. Sebaliknya, password database, token API, private key, dan credential layanan pembayaran harus diperlakukan sebagai secret.
Perbedaan ini penting karena setting biasa masih mungkin disimpan dalam konfigurasi proyek, sementara secret sebaiknya diberikan melalui environment variable, sistem secret manager, atau mekanisme deployment yang memiliki pembatasan akses. OWASP juga menyarankan agar secret memiliki masa hidup yang jelas, dapat dicabut, tidak dicatat ke log, dan hanya bisa diakses oleh pihak yang memang membutuhkannya.
Jangan menganggap file .env otomatis aman. File tersebut hanya salah satu cara memuat konfigurasi. Jika ikut ter-commit ke Git, terbaca oleh web server, masuk ke backup yang tidak terlindungi, atau dapat diakses terlalu banyak orang, secret tetap berisiko bocor.
Ambil konfigurasi secara terpusat
Daripada memanggil getenv() di puluhan file, buat satu lapisan konfigurasi. Dengan begitu, nama variabel, nilai default, validasi, dan aturan wajib diisi berada di satu tempat.
<?php
function requiredEnv(string $name): string
{
$value = getenv($name);
if ($value === false || trim($value) === '') {
throw new RuntimeException("Konfigurasi wajib belum tersedia: {$name}");
}
return $value;
}
$config = [
'app_env' => getenv('APP_ENV') ?: 'production',
'app_debug' => filter_var(
getenv('APP_DEBUG') ?: 'false',
FILTER_VALIDATE_BOOLEAN
),
'db_host' => requiredEnv('DB_HOST'),
'db_name' => requiredEnv('DB_NAME'),
'db_user' => requiredEnv('DB_USER'),
'db_password' => requiredEnv('DB_PASSWORD'),
];Contoh tersebut melakukan dua hal penting. Pertama, aplikasi gagal lebih awal jika konfigurasi kritis tidak tersedia. Kedua, pengembang tidak perlu menebak apakah password kosong berarti memang disengaja atau terjadi kesalahan deployment.
Validasi juga perlu menyesuaikan jenis data. Nilai seperti APP_DEBUG sebaiknya diubah menjadi boolean, bukan dibandingkan secara longgar dengan string. Jika aplikasi memiliki port, timeout, atau batas ukuran upload, validasikan sebagai angka dan periksa apakah nilainya berada dalam rentang yang masuk akal.
Jangan campur konfigurasi dengan koneksi database
File konfigurasi sebaiknya hanya menyusun nilai. Proses membuat koneksi database dapat ditempatkan di lapisan lain. Pemisahan ini membuat pengujian lebih mudah dan mengurangi kemungkinan password ikut tersebar ke seluruh kode.
<?php
$dsn = sprintf(
'mysql:host=%s;dbname=%s;charset=utf8mb4',
$config['db_host'],
$config['db_name']
);
$pdo = new PDO($dsn, $config['db_user'], $config['db_password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);Dengan pola ini, controller atau service tidak perlu mengetahui dari mana password berasal. Ia cukup menerima objek koneksi atau service yang sudah siap digunakan. Ini bukan hanya soal keamanan, tetapi juga soal arsitektur: setiap bagian aplikasi memiliki tanggung jawab yang lebih jelas.
Perhatikan kebocoran yang tidak terlihat
Secret sering bocor bukan karena sengaja ditampilkan di halaman, melainkan karena masuk ke tempat lain. Beberapa sumber kebocoran yang umum antara lain:
- Pesan error yang menampilkan connection string lengkap.
- Log request yang mencatat header
Authorizationatau payload sensitif. - Output
phpinfo()yang dibiarkan aktif di production. - Dump variabel konfigurasi saat debugging.
- File backup, screenshot terminal, dan artifact CI/CD.
- Repository Git beserta riwayat commit lamanya.
Karena itu, hindari mencetak seluruh array konfigurasi ketika mencari masalah. Buat fungsi untuk menyamarkan nilai sensitif atau tampilkan hanya nama variabel yang tersedia. Jika sebuah secret sudah terlanjur masuk ke repository, menghapus baris terbaru saja belum cukup. Credential tersebut perlu dianggap bocor dan segera dirotasi.
Gunakan default dengan hati-hati
Default berguna untuk nilai yang aman, seperti zona waktu atau nama lingkungan lokal. Namun, default berbahaya jika dipakai untuk password, token, atau konfigurasi keamanan.
Contoh yang sebaiknya dihindari adalah:
$dbPassword = getenv('DB_PASSWORD') ?: 'password123';Kode tersebut membuat aplikasi tampak berhasil dijalankan ketika konfigurasi penting sebenarnya tidak tersedia. Lebih baik aplikasi berhenti dengan pesan yang jelas daripada berjalan menggunakan credential lemah yang mungkin terlupakan.
Untuk mode debug, default juga perlu dipikirkan. Di komputer lokal, debug dapat membantu pengembangan. Di production, detail error harus disembunyikan dari pengguna dan diarahkan ke sistem logging yang aksesnya dibatasi.
Rancang alur deployment yang konsisten
Konfigurasi yang baik tidak berhenti di kode. Tim perlu menentukan dari mana setiap nilai berasal pada setiap lingkungan. Misalnya, developer memakai file lokal yang tidak di-commit, staging memakai secret dari CI/CD, sedangkan production memakai secret manager atau fasilitas secret milik platform cloud.
Gunakan nama variabel yang konsisten, dokumentasikan variabel wajib, dan sediakan contoh tanpa nilai rahasia seperti .env.example. Contoh tersebut cukup berisi:
APP_ENV=local
APP_DEBUG=true
DB_HOST=127.0.0.1
DB_NAME=nama_database
DB_USER=nama_user
DB_PASSWORD=Pastikan file contoh tidak berisi password sungguhan. Tambahkan aturan untuk mengabaikan file lokal ke .gitignore, tetapi jangan menjadikan .gitignore sebagai satu-satunya perlindungan. Akses server, pipeline, backup, dan secret manager tetap harus dibatasi.
Apa artinya bagi kita?
Memisahkan konfigurasi dari kode membuat aplikasi lebih mudah dipindahkan, diuji, dan dipulihkan ketika credential harus diganti. Namun, environment variable bukan solusi ajaib. Nilai tersebut tetap bisa terbaca oleh proses, pipeline, atau administrator yang memiliki akses terlalu luas.
Langkah praktis yang bisa dilakukan sekarang adalah membuat satu konfigurasi terpusat, menandai mana variabel yang wajib diisi, menghapus secret dari source code, memeriksa log agar tidak merekam credential, lalu menguji deployment dengan konfigurasi yang sengaja tidak lengkap. Jika aplikasi gagal dengan pesan yang jelas dan tidak membocorkan nilai sensitif, fondasi konfigurasi Anda sudah jauh lebih baik.
Sumber & bacaan lebih lanjut
– Rio Yotto @rioyotto
