Role-Based Access Control menyediakan bahan bakunya; least privilege (hak minimum) adalah disiplin memakainya. Prinsipnya satu kalimat: berikan setiap user hak yang paling sedikit — dan paling sempit cakupannya — yang masih membuat pekerjaannya selesai. Chapter ini menerjemahkan prinsip itu menjadi kebiasaan konkret: satu user per layanan, matriks hak yang bisa diaudit, pembatasan sumber koneksi, serta rotasi dan tinjauan berkala.
Peta materi Chapter ini:
- Mengapa hak berlebih adalah utang keamanan yang pasti ditagih.
- Matriks aplikasi → user → role sebagai dokumen rancangan hak akses.
authenticationRestrictionsuntuk membatasi dari mana user boleh masuk.- Rutinitas rotasi kredensial dan audit daftar user.
Hak Berlebih Adalah Utang Keamanan #
Setiap hak yang tidak dipakai tetap menjadi pintu yang bisa dimasuki. Jika aplikasi katalog hanya perlu membaca data tetapi user-nya memegang readWrite, maka satu celah injection pada aplikasi itu otomatis menjadi celah penghapusan data. Least privilege memperkecil blast radius: ketika satu kredensial bocor, kerusakan yang bisa dilakukan dibatasi oleh role yang dipegang user itu — bukan oleh niat penyerangnya.
Prinsip ini juga berlaku pada cakupan database. Aplikasi yang hanya bekerja di latihan_toko tidak perlu readWriteAnyDatabase; cukup readWrite yang di-scope ke satu database itu. Dan orang berbeda dari mesin: admin manusia memakai akun pribadi yang bisa diaudit, bukan akun bersama yang bisa dipakai siapa pun.
Matriks Aplikasi ke Role #
Rancangan hak akses yang baik bisa dibaca dari satu tabel. Berikut contoh matriks untuk sebuah toko buku daring dengan empat “pemain” — dua aplikasi, satu tugas terjadwal, satu operator manusia:
| Pemain | User | Role | Alasan |
|---|---|---|---|
| API utama | toko_api | readWrite di latihan_toko | CRUD pesanan, stok, katalog |
| Dashboard penjualan | toko_dashboard | read di latihan_toko | Grafik dan laporan tanpa perlu menulis |
| Tugas pencadangan | toko_backup | backup di admin + clusterMonitor | Menjalankan mongodump terjadwal |
| Operator manusia | siti_ops | dbOwner di latihan_toko | Perbaikan index dan schema saat insiden |
Dua baris pertama bisa kita bangun sungguhan di server latihan:
use latihan_uji
// hapus sisa percobaan sebelumnya bila ada
try { db.dropUser("toko_api"); db.dropUser("toko_dashboard") } catch (e) { print("bersih — buat baru") }
db.createUser({
user: "toko_api",
pwd: "sandiToko123",
roles: [{ role: "readWrite", db: "latihan_toko" }]
})
db.createUser({
user: "toko_dashboard",
pwd: "sandiDash123",
roles: [{ role: "read", db: "latihan_toko" }]
})
db.getUsers()Output yang Dihasilkan (bagian $clusterTime dan operationTime yang berulang diringkas):
switched to db latihan_uji
bersih — buat baru
{ ok: 1 }
{ ok: 1 }
{
users: [
{
_id: 'latihan_uji.toko_api',
userId: UUID('00c1a57a-19d9-41da-9337-d81b2fef572a'),
user: 'toko_api',
db: 'latihan_uji',
roles: [ { role: 'readWrite', db: 'latihan_toko' } ],
mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
},
{
_id: 'latihan_uji.toko_dashboard',
userId: UUID('0dd853b9-8f65-4904-9a41-4f00ef7ec4b2'),
user: 'toko_dashboard',
db: 'latihan_uji',
roles: [ { role: 'read', db: 'latihan_toko' } ],
mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
}
],
ok: 1
}Perhatikan polanya: dua aplikasi yang bekerja pada database yang sama tetap memegang role berbeda sesuai kebutuhan masing-masing. Ketika dashboard diretas lewat dependency yang bocor, penyerangnya hanya bisa membaca — tidak bisa menghapus stok atau mengubah harga.
Membatasi Dari Mana User Boleh Masuk #
Least privilege juga punya dimensi jaringan. Field authenticationRestrictions pada db.createUser() membatasi alamat IP atau rentang CIDR asal koneksi yang boleh mengautentikasi sebagai user tersebut — kredensial yang dicuri dari luar jaringan aplikasi menjadi tidak bisa dipakai:
db.createUser({
user: "toko_cron",
pwd: "sandiCron123",
roles: [{ role: "read", db: "latihan_toko" }],
authenticationRestrictions: [
{ clientSource: ["127.0.0.1"] }
]
})
db.getUser("toko_cron", { showAuthenticationRestrictions: true })Output yang Dihasilkan (diringkas serupa; daftar actions pada inheritedPrivileges juga dipadatkan karena isinya persis privilege role read yang kita bedah di Chapter Role-Based Access Control):
{ ok: 1 }
{
_id: 'latihan_uji.toko_cron',
userId: UUID('509ed15b-485f-477a-b476-99c405b2fa8b'),
user: 'toko_cron',
db: 'latihan_uji',
mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ],
authenticationRestrictions: [ { clientSource: [ '127.0.0.1' ] } ],
roles: [ { role: 'read', db: 'latihan_toko' } ],
inheritedRoles: [ { role: 'read', db: 'latihan_toko' } ],
inheritedPrivileges: [
{
resource: { db: 'latihan_toko', collection: '' },
actions: [ 'changeStream', 'collStats', 'dbHash', 'dbStats', 'find',
'killCursors', 'listCollections', 'listIndexes', 'listSearchIndexes',
'planCacheRead', 'performRawDataOperations' ]
},
{
resource: { db: 'latihan_toko', collection: 'system.js' },
actions: [ '... daftar actions yang sama ...' ]
}
],
inheritedAuthenticationRestrictions: []
}User toko_cron kini hanya bisa login dari 127.0.0.1 — cocok untuk tugas terjadwal yang berjalan di mesin yang sama dengan server. Di produksi, ganti nilainya dengan IP atau CIDR jaringan internal tempat layanan itu berada, misalnya "10.0.0.0/24". Detail batasan hanya tampil pada query user bentuk tepat seperti db.getUser() di atas — db.getUsers() menolak menampilkannya.
Rotasi dan Audit Berkala #
Least privilege bukan keadaan yang sekali dipasang lalu dilupakan. Aplikasi bertambah, kebutuhan bergeser, dan tanpa disadari user lama menumpuk hak yang tidak lagi dipakai. Jadikan tiga rutinitas ini bagian dari kalender operasional:
- Rotasi kredensial: ganti password user aplikasi secara berkala (misalnya tiap 90 hari) dan segera saat ada indikasi bocor — pakai
db.changeUserPassword()dari Chapter Database Users. - Tinjauan hak: telusuri
db.getUsers()di tiap database dandb.getRole()pada role kustom; pertanyakan setiap role yang tidak jelas pemakainya. - Pembersihan: jatuhkan user milik layanan yang sudah pensiun dengan
db.dropUser()— user menganggur adalah pintu yang terlupakan.
Bersihkan kembali percobaan kita:
db.dropUser("toko_api")
db.dropUser("toko_dashboard")
db.dropUser("toko_cron")
db.getUsers()Output yang Dihasilkan (diringkas serupa):
{ ok: 1 }
{ ok: 1 }
{ ok: 1 }
{
users: [],
ok: 1
}Rangkuman #
- Least privilege: hak paling sedikit dan cakupan paling sempit yang masih menyelesaikan pekerjaan — untuk memperkecil dampak kredensial yang bocor.
- Satu user per layanan, didokumentasikan dalam matriks aplikasi → user → role yang bisa diaudit sekilas.
authenticationRestrictionsmenambah dimensi jaringan: user hanya boleh masuk dari IP/CIDR tertentu.- Rotasi password, tinjauan
getUsers()/getRole(), dan pembersihan user pensiun adalah rutinitas berkala, bukan tugas sekali jalan.