Langsung ke konten
Environment Variable dan Secrets Management: Praktik Aman dari Dev sampai Produksi yang Saya Pakai Sekarang
Kembali ke Blog
DevOps·6 menit baca·

Environment Variable dan Secrets Management: Praktik Aman dari Dev sampai Produksi yang Saya Pakai Sekarang

Environment variable memang praktis, tapi berisiko bocorkan secrets di production. Ini pola secrets management yang saya pakai dari lokal sampai deploy, plus kesalahan yang pernah saya lakukan.

TH
Tomi Hartanto·Senior Software Engineer

Beberapa tahun lalu saya menemukan file .env lama sebuah proyek ter-commit di history Git. Bukan di working tree — sudah lama dihapus — tapi masih utuh di commit dua tahun sebelumnya, lengkap dengan credential database production. Password sudah sempat dipakai, dan tidak ada yang sadar. Insiden kecil itu mengubah cara saya memperlakukan environment variable: bukan sebagai tempat penyimpanan secrets, tapi sekadar mekanisme konfigurasi.

Miskonsepsi yang paling sering saya lihat di tim-tim yang pernah saya bantu adalah menganggap "env var = secrets management". Padahal dua-duanya masalah berbeda, dan menggabungkannya bikin keduanya dikelola dengan buruk.

Kenapa Environment Variable Itu Problem untuk Secrets

Env var terasa aman karena "tidak tertulis di kode". Tapi di praktiknya, env var bocor lewat banyak celah yang jarang orang pikirkan:

  • Crash report dan error tracking. Sentry, Bugsnag, atau tools sejenis sering menangkap seluruh environment proses saat exception terjadi. Kalau DB_PASSWORD ada di env, dia ikut terkirim.
  • Diagnostics dan child process. docker inspect, /proc/<pid>/environ, dan subprocess yang mewarisi environment semuanya membaca env var dengan mudah.
  • Logging CI/CD dan tooling pihak ketiga. Ini bukan teori: ada laporan bahwa Postman me-log environment variable penggunanya — tool yang justru dipakai banyak developer untuk menyimpan token API.

Diskusi klasik di MovingFast, Environment Variables Considered Harmful for Your Secrets, merangkum argumennya dengan rapi: env var adalah media broadcast — semua proses child, semua tooling, semua crash reporter bisa membacanya, tanpa audit trail siapa yang mengakses.

Diagram env var bocor ke crash report, docker inspect, dan child process

Yang saya praktikkan sekarang sederhana: env var untuk konfigurasi non-sensitif, secrets manager untuk credential. NODE_ENV, PORT, LOG_LEVEL, URL database tanpa password — semua boleh env var. Password, API key, private key — tidak.

Pola yang Saya Pakai di Setiap Proyek

Level lokal: .env hanya untuk dev, dan harus di-gitignore dari hari pertama

File .env masih saya pakai untuk development lokal — itu tidak masalah, asalkan isinya hanya dummy credential dan memang sudah masuk .gitignore sebelum commit pertama. Yang saya tambahkan di setiap repo sekarang adalah file .env.example:

# .env.example — commit ke repo, tanpa nilai asli
DATABASE_URL=postgres://app:app@localhost:5432/app_dev
STRIPE_API_KEY=sk_test_xxx

Dosen nggak, tapi kesalahan klasik yang tetap saya lihat berulang: .gitignore ditambahkan setelah .env sudah ter-commit. git rm --cached .env baru menghapus dari working tree — history tetap menyimpannya, persis kasus yang saya alami di pembuka artikel ini.

Level produksi: secrets lewat file, bukan env var

Untuk aplikasi Node.js di production, pola favorit saya saat ini adalah yang direkomendasikan komunitas keamanan Node: simpan secrets sebagai file, injek saat runtime, baca via fs.readFileSync. Prinsipnya dibahas panjang di artikel Node.js Security, Do not use secrets in environment variables.

Konkretnya di aplikasi saya:

// config.ts
import fs from 'node:fs';

function readSecret(name: string): string {
  const path = process.env[`${name}_FILE`]; // mis. DB_PASSWORD_FILE=/run/secrets/db_password
  if (path) return fs.readFileSync(path, 'utf8').trim();
  // fallback untuk dev lokal saja
  return process.env[name] ?? '';
}

export const dbPassword = readSecret('DB_PASSWORD');

Dengan Docker Compose, file itu bisa dipasang via Docker Secrets atau bind mount read-only. Di Kubernetes, equivalennya Secret yang dimount sebagai volume — bukan Secret yang didecode ke env var, karena itu kembali ke masalah awal. Untuk deployment di Google App Engine, ada trik menarik di Stack Overflow soal menyimpan app.yaml berisi env di bucket GCS lalu men-source-nya saat build — pattern yang valid, walau saya lebih memilih platform yang punya secrets manager native.

Ilustrasi secrets sebagai file dimount ke container, dibaca via filesystem

Level tim: rotation dan audit itu yang membedakan "disimpan" dari "dikelola"

Menyimpan secrets di AWS Secrets Manager atau GCP Secret Manager bukan cuma soal enkripsi. Dua fitur yang paling sering menyelamatkan saya:

  1. Audit trail — saya bisa jawab "token ini diakses kapan dan oleh siapa" saat ada investigasi.
  2. Rotation — credential database saya set rotate tiap 30 hari. Di proyek multi-tenant, di mana satu credential salah bisa menyentuh data semua tenant, ini bukan opsional.

Satu hal yang perlu diakui: rotation itu menyusahkan. Connection string yang berubah tiap 30 hari berarti aplikasi harus bisa reload credential tanpa restart, dan itu berinteraksi langsung dengan cara Anda mengelola connection pool — pool yang di-cache dengan credential lama akan tiba-tiba gagal auth. Solusi saya: baca secret saat membuat koneksi baru, dan recycling pool secara berkara. Tidak elegan, tapi jujur.

Kapan Env Var untuk Secrets Masih Wajar

Saya tidak dogmatis. Untuk proyek internal kecil, prototype, atau environment yang sudah terisolasi (misalnya ephemeral CI runner), env var untuk secrets masih masuk akal — risikonya proporsional dengan nilainya. Trade-offnya sadari saja: tidak ada audit trail, tidak ada rotation, dan satu console.log(process.env) yang lupa dihapus bisa membocorkan semuanya ke log agregator.

Yang tidak bisa ditoleransi lagi di 2024: secrets di Git (termasuk history), secrets di tool pihak ketiga tanpa enkripsi end-to-end, dan secrets yang sama dipakai dev dan production. Tiga-duanya pernah saya temukan di codebase yang production-nya menangani data customer sungguhan.

Takeaway yang Bisa Langsung Anda Pakai

Kalau mau mulai hari ini tanpa migrasi besar:

  1. Jalankan git log --all --full-history -- .env di repo Anda sekarang. Kalau ada hasil, anggap credential itu sudah bocor dan rotate.
  2. Pisahkan dua kebutuhan: config non-sensitif tetap env var, credential pindah ke file + pattern *_FILE di atas. Perubahannya kecil — satu helper function.
  3. Set rotation untuk minimal credential database dan API key payment. Mulai dari 90 hari kalau 30 hari terlalu agresif untuk tim Anda.

Migrasi ke pola file + secrets manager di proyek terakhir saya butuh waktu sekitar dua hari, dan sebagian besar waktunya habis untuk rotation logic — bukan untuk pola baca filenya sendiri. Dua hari itu murah dibanding sekali saja menjelaskan ke customer kenapa API key mereka ada di log publik.

Sumber

TH

Tomi Hartanto

Senior Software Engineer

Backend developer dengan 8+ tahun pengalaman membangun sistem scalable. Menulis tentang arsitektur backend, database, dan DevOps.