Sensitive data exposure adalah bocornya data sensitif karena gak sengaja terkirim atau tercatat. Seringnya bukan karena dibobol, tapi karena kode mengirim lebih banyak dari yang perlu: response API yang memuat semua kolom, pesan error yang terlalu detail, atau log yang mencatat password. Bayangin kasir yang, saat memberi struk, ikut mencetak PIN dan saldo lengkapmu di situ. Gak ada yang membobol, kasirnya sendiri yang membocorkan.
🧪 Tebakan cepat: sebuah endpoint
GET /api/user/5menjalankanres.json(user)denganuserdari database apa adanya. Objek user punya kolompassword_hashdanssn. Apa yang ikut terkirim ke browser? Tebak dulu, jawabannya ada di bagian "Sumber kebocoran paling umum".
Jenis data sensitif
Data yang gak boleh terekspos ke pihak yang gak berhak:
- Credentials: password, API key, kredensial database.
- Data pribadi: NIK, nomor kartu kredit, alamat, nomor telepon.
- Token: session token, JWT, OAuth token.
- Info internal: stack trace, query SQL, path server.
Sumber kebocoran paling umum
1. Response API yang berlebihan. Mengirim seluruh objek dari database, termasuk kolom yang sensitif:
// BURUK: kirim semua kolom apa adanya
app.get("/api/user/:id", async (req, res) => {
const user = await User.findById(req.params.id);
res.json(user); // ikut password_hash, ssn, internal id!
});
// BAIK: pilih hanya kolom yang perlu (allowlist)
app.get("/api/user/:id", async (req, res) => {
const user = await User.findById(req.params.id)
.select("name email avatar createdAt");
res.json(user);
});
- Versi buruk mengirim
userapa adanya. Kalau objek itu punyapassword_hashdanssn, keduanya ikut ke browser. Itu jawaban tebakannya: semuanya terkirim. - Versi baik memakai allowlist: sebut kolom yang boleh keluar, bukan mencoba membuang yang sensitif. Allowlist lebih aman, karena kolom baru yang sensitif gak otomatis ikut bocor.
2. Pesan error yang terlalu detail. Mengirim detail internal ke client:
// BURUK: bocorkan struktur database
{ "error": "SQLSTATE[42S02]: Table 'db_prod.users' doesn't exist" }
// BAIK: pesan generik ke user, detail dicatat di log server
{ "error": "Terjadi kesalahan internal. Silakan coba lagi." }
Stack trace di response memberi penyerang peta: nama file, library, versi, bahkan query. Di production, tampilkan pesan generik dan simpan detail di log server, bukan di response.
3. Logging yang tidak aman. Mencatat data sensitif ke log:
// BURUK: password terekspos di file log
console.log("Login attempt:", { email, password });
// BAIK: redact data sensitif
console.log("Login attempt:", { email, password: "[REDACTED]" });
4. File sensitif yang terekspos. File ini gak boleh bisa diakses publik:
.env # kredensial dan secret
.git/ # seluruh sejarah kode
backup.sql # dump database
phpinfo.php # info server
Pencegahan
- Pakai API Resource / DTO untuk mengontrol kolom yang dikirim (allowlist).
- Set APP_DEBUG=false di production, supaya stack trace gak muncul.
- Redact data sensitif sebelum di-log.
- Konfigurasi web server untuk memblokir akses ke file sensitif.
- Pakai HTTPS untuk semua komunikasi (data in transit), dan enkripsi data sensitif di database (data at rest).
- Review sejarah git untuk memastikan gak ada secret yang pernah ter-commit.
Laravel: sembunyikan kolom dengan $hidden
Di Laravel, property $hidden pada Model mencegah kolom muncul saat model di-serialize ke JSON:
class User extends Authenticatable
{
protected $hidden = [
"password",
"remember_token",
];
}
- Setiap kali User berubah jadi JSON (misalnya
return $user;dari controller),passworddanremember_tokenotomatis dibuang. remember_tokenadalah token "ingat saya" yang, kalau bocor, bisa dipakai login sebagai user itu. Makanya ikut disembunyikan.- Ini lapisan jaring pengaman, bukan pengganti allowlist. Untuk kontrol penuh, pakai API Resource yang menyebut persis kolom yang keluar.
⚠️ Jebakan yang sering ketemu
- Mengirim seluruh objek user.
res.json(user)ikut membawapassword_hashdan kolom internal. Sebut kolom yang boleh keluar. - Menampilkan stack trace ke user. Set
APP_DEBUG=falsedi production, dan kembalikan pesan generik. - Mencatat password atau token di log. Redact dulu jadi
[REDACTED]sebelumconsole.log. - Blacklist, bukan allowlist. Membuang kolom sensitif satu per satu gampang lupa. Sebut yang boleh keluar, bukan yang harus dibuang.
TL;DR: Sensitive data exposure adalah data sensitif (password, token, NIK, info internal) yang bocor karena terkirim atau tercatat tanpa sengaja. Sumbernya: response API yang mengirim seluruh objek (termasuk password_hash, ssn), pesan error dengan stack trace, log yang mencatat password, dan file seperti .env yang bisa diakses publik. Perbaikannya: allowlist kolom lewat API Resource/DTO, APP_DEBUG=false dan pesan error generik di production, redact log, blokir file sensitif, HTTPS, dan enkripsi at rest. Di Laravel, $hidden (misal password, remember_token) menyembunyikan kolom dari JSON. Pakai allowlist, bukan blacklist.