Secret bocor karena Next.js mengirim semua kode Client Component dan variabel NEXT_PUBLIC_ ke browser. Perbaiki dengan rotasi secret lama, hapus prefix NEXT_PUBLIC_ dari token rahasia, lalu pindahkan pembacaan process.env ke Server Component, Server Action, atau Route Handler. Client hanya menerima hasil akhir yang aman.
Penyebab Secret Bocor ke Client di App Router
App Router memisahkan kode server dan browser secara tegas. Secret bocor ketika batas itu dilanggar secara tidak sengaja.
- NEXT_PUBLIC_ dipakai untuk token, password, dan API secret, padahal prefix itu memang disematkan ke bundle JavaScript.
- File dengan use client mengimpor secret langsung atau mengimpor modul server yang membaca secret.
- Server Component meneruskan credential lewat props ke Client Component, lalu props itu tampil di React Flight payload.
- Route Handler mengembalikan process.env.API_SECRET sebagai JSON untuk debugging dan terbaca di Network tab.
- console.log token di browser atau menyertakan header Authorization pada fetch yang dijalankan dari client.
Tabel Cek Cepat Sumber Kebocoran Secret
Periksa tiga lokasi ini sebelum mengubah kode: bundle, respons API, dan definisi ENV.
| Masalah | Penyebab | Solusi |
|---|---|---|
| Token terlihat di bundle JS | Memakai NEXT_PUBLIC_API_SECRET | Ganti ke ENV server tanpa prefix |
| Props berisi password | Server Component mengirim credential | Kirim hasil olahan saja ke client |
| Header rahasia di Network | Fetch dari client membawa secret | Pindah fetch ke Server Action |
- Buka DevTools, tab Sources, cari nama secret di file .js hasil build.
- Buka tab Network, periksa respons Route Handler dan payload Server Component.
- Bandingkan ENV server seperti API_SECRET dengan ENV publik seperti NEXT_PUBLIC_API_URL.
Langkah Memperbaiki Secret yang Terlanjur Bocor
- Cabut dan rotasi secret yang sudah tampil di client. Secret yang pernah masuk bundle dianggap sudah terbaca publik.
- Pindah ENV rahasia tanpa prefix NEXT_PUBLIC_. Contoh aman: API_SECRET, DATABASE_URL, AUTH_SECRET.
- Pindah akses secret ke Server Component, Server Action, dan Route Handler. Jangan impor modul berisi secret dari Client Component.
- Validasi input di server, lalu kirim hasil aman ke client. Client boleh mengirim ID atau form, bukan token.
- Gunakan pola server-only berikut:
import "server-only";
export async function getOrders(userId: string) {
const res = await fetch("https://api.internal/orders", {
headers: { authorization: `Bearer ${process.env.API_SECRET}` },
cache: "no-store",
});
if (!res.ok) throw new Error("Gagal memuat order");
return res.json();
}
Panggil fungsi itu dari Server Component atau Server Action. Browser hanya menerima data order, bukan nilai API_SECRET.
FAQ
Apakah NEXT_PUBLIC_ aman untuk API key rahasia?
Tidak. Semua variabel itu disematkan ke bundle browser dan bisa dibaca siapa pun.
Bagaimana cek secret sudah bocor?
Cari secret di bundle JS production, respons API, dan log browser menggunakan kata kunci nama ENV.
Apakah Server Action otomatis aman?
Tidak. Kode berjalan di server, tetapi input tetap harus divalidasi dan akses pengguna dibatasi.