Aplikasi yang kita bangun sepanjang Course ini hampir semuanya menyimpan sesuatu yang tidak boleh dibaca sembarang orang: kata sandi database, API key layanan pihak ketiga, kunci penandaan token. Aset-aset itu — kolektifnya disebut secret — menempati posisi unik dalam keamanan: satu-satunya kategori aset yang bahayanya bukan hanya bocor ke mana, melainkan juga tertulis di mana. Chapter penutup Section ini membereskan kebiasaan yang paling sering menjadikan aplikasi tidak aman sejak baris pertama: secret yang tertanam langsung di dalam kode. Kita akan memindahkannya ke environment variable, memuatnya lewat berkas .env, menjauhkannya dari version control, lalu menutup dengan praktik rotasi.

Secret yang Tertanam di Kode #

Bentuk yang paling sering dijumpai di proyek pemula tampak tidak berbahaya — konstanta yang dipakai di dua tempat:

js Anti-pattern: secret tertanam di dalam kode (jangan ditiru)
const API_TOKEN = 'YOUR_PAYMENT_API_TOKEN'; // di kode nyata: nilai sungguhan
const koneksi = 'postgresql://budi:sandiRahasia@localhost:5432/toko';

await fetch(`https://api.example.com/v1/tagihan`, {
  headers: { Authorization: `Bearer ${API_TOKEN}` },
});

Masalahnya bukan hari ini, melainkan besok, ketika kode ini masuk ke version control. Setiap commit menyimpan salinan berkas secara permanen — menghapus berkas pada commit berikutnya tidak menghapus riwayatnya. Seluruh anggota tim yang pernah mendapat akses repository ikut memegang secret; begitu pula setiap cadangan, setiap hasil cloning, dan setiap layanan CI yang membaca kode. Mengganti secret pun menjadi operasi mahal: nilai baru berarti kode diubah, di-commit, lalu dideploy ulang — padahal kebocoran menuntut pergantian secepatnya. Kontras dengan itu, secret yang hidup di luar kode dipindahkan cukup dengan memperbarui satu variabel di lingkungan deploy dan memulai ulang proses.

Pindah ke Environment Variable #

Mekanisme bawaan untuk nilai di luar kode adalah environment variable: pasangan kunci-nilai yang disiapkan sistem operasi saat proses dimulai, terbaca di Node.js melalui object process.env . Kita telah memakai mekanisme ini pada Chapter Environment Variables di Section Command Line Applications; sekarang kita menegakkannya dengan satu aturan keamanan: setiap secret wajib masuk lewat process.env, dan setiap nilai wajib diperiksa keberadaannya sebelum aplikasi menyala:

js
// Membaca konfigurasi dari environment variable.
// Bila secret wajib tidak ada, aplikasi menolak menyala (fail securely).
const token = process.env.API_TOKEN;

if (token === undefined || token === '') {
  console.error('API_TOKEN tidak ditemukan - aplikasi menolak menyala.');
  process.exitCode = 1;
} else {
  console.log('API_TOKEN terbaca :', token.slice(0, 4) + '...', `(${token.length} karakter)`);
  console.log('NAMA_APLIKASI     :', process.env.NAMA_APLIKASI ?? '(tidak diatur)');
  console.log('KONEKSI_DB        :', process.env.KONEKSI_DB ? 'terpasang' : '(tidak diatur)');
}

Output yang Dihasilkan:

text
API_TOKEN tidak ditemukan - aplikasi menolak menyala.

Dijalankan tanpa persiapan apa pun, skrip ini menolak menyala dengan kode keluar 1 — penerapan langsung prinsip fail securely dari chapter pertama: konfigurasi yang hilang bukan alasan aplikasi berjalan setengah hidup tanpa autentikasi. Tiga baris terakhir juga menunjukkan kebiasaan pencatatan yang aman: secret tidak pernah dicetak utuh — hanya awalannya dan panjangnya, cukup untuk memastikan nilainya terbaca.

Berkas .env dan Flag –env-file #

Mengetik puluhan export di terminal setiap kali menjalankan aplikasi tentu tidak praktis. Sejak Node.js 20 ada solusi bawaan: flag node --env-file memuat pasangan kunci-nilai dari sebuah berkas ke process.env saat proses dimulai — tanpa library tambahan. Berikut isi berkas konfigurasi latihan kita (di proyek nyata berkas seperti ini diberi nama .env):

text
# Berkas konfigurasi latihan - SELURUH nilai adalah placeholder,
# bukan secret sungguhan. Di proyek nyata berkas seperti ini
# diberi nama .env dan TIDAK ikut disimpan di version control.
NAMA_APLIKASI=Toko Latihan
API_TOKEN=YOUR_API_TOKEN
KONEKSI_DB=postgresql://budi:YOUR_DATABASE_PASSWORD@localhost:5432/toko

Sekarang jalankan baca-konfigurasi.mjs dengan berkas tersebut:

bash
node --env-file=contoh-env.txt baca-konfigurasi.mjs

Output yang Dihasilkan:

text
API_TOKEN terbaca : YOUR... (14 karakter)
NAMA_APLIKASI     : Toko Latihan
KONEKSI_DB        : terpasang

Aplikasi menyala lengkap dengan seluruh konfigurasinya, dan nilai placeholder YOUR_API_TOKEN terbaca sebagai 14 karakter. Satu perilaku flag ini yang wajib diketahui:

bash
API_TOKEN=DARI_SHELL node --env-file=contoh-env.txt baca-konfigurasi.mjs

Output yang Dihasilkan:

text
API_TOKEN terbaca : DARI... (10 karakter)
NAMA_APLIKASI     : Toko Latihan
KONEKSI_DB        : terpasang

Menjauhkan Berkas dari Version Control #

Berkas .env memindahkan secret keluar dari kode — langkah pertama. Langkah keduanya memastikan berkas itu sendiri tidak masuk ke repository:

text Isi .gitignore untuk proyek dengan berkas .env
node_modules/
.env
.env.*
!.env.example

Pola tiga baris terakhir adalah konvensi yang perlu kita biasakan: .env dan semua variasinya (env.local, .env.production) diabaikan Git, tetapi berkas contoh justru dikecualikan dari pengecualian lewat !.env.example. Berkas contoh itu berisi seluruh kunci dengan nilai placeholder semacam YOUR_API_TOKEN — cukup bagi rekan tim baru untuk menyiapkan konfigurasi lokalnya tanpa pernah melihat nilai sungguhan. Chapter Struktur Proyek Node.js di Section Persiapan Lingkungan membahas pola .gitignore ini dari sisi struktur; sudut keamanannya ada di sini.

Rotasi dan Skala Besar #

Rotasi adalah mengganti nilai secret dengan yang baru secara terjadwal atau segera setelah kecurigaan kebocoran. Prosedurnya selalu tiga langkah: terbitkan nilai baru di sumbernya (dasbor penyedia layanan, server database), perbarui environment pada tempat aplikasi berjalan, lalu mulai ulang proses — dan hanya setelah semuanya berjalan, cabut nilai lama. Karena aplikasi kita hanya membaca process.env, tidak ada satu baris kode pun yang berubah; inilah buah nyata dari memisahkan secret dari kode. Pada sistem besar, penyimpanan khusus seperti Docker Secrets atau layanan secret manager milik penyedia cloud menambahkan lapisan berupa enkripsi at-rest, pencatatan akses per secret, dan penerbitan sementara — tetapi prinsip antarmukanya tetap sama: aplikasi membaca dari satu pintu terkelola, bukan dari tubuhnya sendiri.

Delapan chapter Section ini telah melengkapi aplikasi kita dari fondasi prinsip sampai pengelolaan kredensial: masukan divalidasi, query diikat, output dienkode, proses dipanggil aman, permintaan dibatasi, dan secret dipisahkan. Pertanyaan berikutnya wajar muncul: bagaimana kita memastikan seluruh pertahanan itu tetap bekerja seiring aplikasi tumbuh? Jawabannya adalah pengujian otomatis — materi Section Testing.