Masalah dan Skenario yang Berlaku
Layanan dokumen B2B menggunakan basis data bersama (shared database). Seorang pengguna dapat menjadi anggota dari beberapa tenant dan memiliki peran viewer, editor, atau admin di masing-masing tenant tersebut. Sebuah dokumen juga dapat dibagikan secara langsung kepada anggota dari tenant yang sama. Layanan ini menyediakan API untuk membaca, memperbarui, dan menghapus dokumen tunggal, ekspor massal, serta pembuatan arsip asinkron. Validasi tanda tangan, masa berlaku, dan audiens JWT sudah berfungsi, tetapi kode warisan (legacy code) memuat dokumen secara global berdasarkan document_id yang diberikan oleh klien dan kemudian hanya memeriksa apakah permintaan tersebut terautentikasi.
Seorang penyerang memperoleh UUID yang valid dari entri audit, tautan berbagi, atau API lain lalu mengganti ID dokumen miliknya dengan ID tersebut. UUID memang sulit untuk dienumerasi, tetapi server tetap akan membocorkan atau memodifikasi dokumen tersebut kecuali jika server memeriksa apakah subjek saat ini diizinkan melakukan tindakan ini pada sumber daya ini di dalam tenant yang sedang aktif. OWASP menyebut kerentanan ini Broken Object Level Authorization (BOLA); materi keamanan web umum juga menggunakan istilah Insecure Direct Object Reference (IDOR). Pengidentifikasi objek dapat muncul di path, parameter kueri, header, JSON body, variabel GraphQL, nama file, atau daftar massal.
Dua halaman persiapan wawancara API publik secara eksplisit meminta kandidat untuk menjelaskan atau menguji IDOR. Diskusi publik pada Januari 2026 menanyakan apakah mengenkripsi ID objek dapat memperbaiki IDOR, yang menunjukkan bahwa “menyembunyikan ID” masih menjadi kesalahpahaman praktis yang perlu dianalisis oleh kandidat. OWASP API Security Top 10 API1:2023 dan studi empiris publik tahun 2026 memberikan konteks risiko dan teknis tambahan. Bukti ini mendukung representasi materi; hal ini tidak menetapkan pertanyaan atau frekuensi wawancara di perusahaan tertentu.
Ini adalah pertanyaan backend karena tugas utamanya adalah meneruskan identitas terautentikasi, relasi tenant, atribut sumber daya, dan tindakan ke dalam satu batas otorisasi di seluruh API, akses data, transaksi, cache, dan worker. Artikel OAuth yang ada membahas identitas dan alur authorization-code, SQL injection membahas struktur kueri, dan SSRF membahas otorisasi destinasi keluar (outbound). Tidak ada satupun yang menjawab akses tingkat objek.
Hal yang Dievaluasi oleh Pewawancara
Sinyal pertama adalah ketepatan terminologi. Autentikasi menjawab "siapa yang memanggil?" Otorisasi tingkat objek menjawab "apakah subjek ini boleh melakukan tindakan ini pada objek ini sekarang?" Pengguna yang boleh memanggil PATCH /documents/{id} tetapi tidak boleh memodifikasi dokumen milik tenant lain menunjukkan adanya BOLA. Anggota biasa yang dapat mengakses fungsi hapus massal khusus admin lebih condong ke Broken Function Level Authorization. Membiarkan pengguna memperbarui properti owner_id yang dilindungi pada dokumen mereka sendiri adalah masalah otorisasi properti objek.
Sinyal kedua adalah kelengkapan model otorisasi. Hanya membandingkan document.owner_id == user.id akan mengabaikan peran tenant, pembagian tim, hak akses baca-saja, pencabutan izin, dan akses dukungan teknis sementara. Jawaban yang kuat membuat satu keputusan secara eksplisit:
allow(subject, tenant, action, resource, context) -> decision + reasonTenant diperoleh dari keanggotaan yang telah diverifikasi. Tindakan membedakan antara read, update, delete, share, dan export. Atribut sumber daya mencakup tenant, status, pemilik, dan relasi pembagian. Konteks dapat berisi izin dukungan teknis sementara dan versi kebijakan. Jika tidak ada aturan izinkan (allow rule) yang cocok, keputusannya adalah tolak (deny).
Sinyal ketiga adalah penerapan batas di tempat data dipilih dan diubah. Memuat data secara global dengan findById(id) dan mengandalkan satu controller untuk menambahkan pemeriksaan menciptakan celah bypass melalui endpoint massal, worker, cache, dan rute baru. Pendekatan yang direkomendasikan adalah melakukan kueri dari kumpulan data yang telah diotorisasi. Mutasi menyertakan kondisi cakupan dan versi ke dalam pernyataan atau transaksi yang sama serta memverifikasi jumlah baris yang terpengaruh.
Sinyal keempat adalah pemahaman terhadap batasan defense-in-depth. UUID acak, respons 404, rate limit, dan PostgreSQL Row-Level Security (RLS) semuanya membantu, tetapi tidak ada yang menggantikan otorisasi bisnis penuh. Dokumentasi PostgreSQL 18 juga menyatakan bahwa superuser, peran BYPASSRLS, dan biasanya pemilik tabel dapat melewati RLS. Pengaktifan kebijakan, peran koneksi, dan konteks tenant per-permintaan harus diverifikasi.
Sinyal terakhir adalah pembuktian bahwa setiap jalur menolak akses lintas objek. Keberhasilan GET untuk dokumen milik sendiri adalah pengujian fungsional. Pengujian keamanan menggunakan banyak akun, tenant, peran, dan ID objek eksternal valid yang telah diketahui. Pengujian tersebut mencakup operasi baca, tulis, hapus, operasi massal, ekspor, unduhan, GraphQL, hit cache, dan eksekusi worker, serta memastikan tidak ada efek samping pada basis data, penyimpanan objek, atau pengiriman pesan.
Pertanyaan yang Perlu Diklarifikasi Sebelum Menjawab
- Bagaimana tenant dipilih? Pengguna dapat berganti tenant aktif, tetapi
X-Tenant-IDdari klien hanya menyatakan sebuah pilihan. Server harus memvalidasi ulang keanggotaan untuk identitas terautentikasi alih-alih mempercayai header begitu saja. - Apa yang menentukan izin? Apakah hanya peran tenant, atau juga kepemilikan, keanggotaan tim, pembagian langsung, status dokumen, dan izin dukungan teknis sementara? Aturan yang lebih dinamis membutuhkan kebijakan terpusat dan alasan audit yang stabil.
- Tindakan mana yang memerlukan izin terpisah? Hak baca tidak otomatis mencakup unduh, ekspor, bagikan, atau hapus. Operasi massal menerapkan keputusan tingkat tindakan yang sama untuk setiap objek.
- Apakah penolakan harus mengembalikan 403 atau 404? Jika ID objek eksternal tidak boleh mengungkapkan keberadaannya, maka kondisi tidak ditemukan dan tidak terlihat dapat berbagi kontrak respons 404. Objek dari tenant yang sama yang terlihat namun tidak memiliki izin tindakan yang cukup dapat mengembalikan 403 sesuai kontrak produk. Kedua respons tidak boleh memperlihatkan perbedaan sensitif.
- Apakah job menggunakan izin saat ini atau snapshot saat pengajuan? Rancangan ini memeriksa pada saat dimasukkan ke antrean (enqueue) dan sekali lagi saat eksekusi, sehingga ekspor yang diantrekan akan berhenti setelah izin dicabut. Jika diperlukan hak akses snapshot yang tidak dapat diubah (immutable), buat model izin eksplisit dengan cakupan dan batas waktu kedaluwarsa.
- Bisakah staf dukungan teknis mengakses tenant? Jika ya, gunakan jalur just-in-time terpisah yang memerlukan tiket, alasan, persetujuan, batas waktu kedaluwarsa, dan audit penuh. Jangan berikan tombol pengalih admin global permanen pada koneksi aplikasi umum.
- Apakah PostgreSQL RLS akan digunakan? Jawaban ini menggunakan pembatasan cakupan tenant di tingkat aplikasi sebagai kontrol utama dan RLS sebagai perlindungan terhadap kelalaian kueri. Kombinasi lain valid selama peran basis data, konteks pool, migrasi, dan worker tetap mempertahankan jaminannya.
Kerangka Jawaban 30 Detik
“Saya akan mendefinisikan otorisasi sebagai subject + tenant + action + resource + context dan menerapkan penolakan secara default (deny by default). Middleware autentikasi hanya menetapkan subjek. Tenant aktif harus berasal dari keanggotaan yang terverifikasi. Lapisan data tidak mengekspos pencarian dokumen tanpa cakupan (unscoped): pembacaan menggunakan tenant_id + document_id, diikuti oleh kebijakan tindakan berdasarkan peran, pemilik, dan pembagian. Pembaruan dan penghapusan menyertakan kondisi tersebut ke dalam pernyataan atau transaksi yang sama; jika baris yang terpengaruh nol, tangani seolah-olah objek tidak terlihat. UUID mengurangi enumerasi tetapi tidak pernah menggantikan kontrol akses.
Jalur operasi massal, cache, unduhan, dan ekspor menggunakan aturan yang sama. Kunci cache menyertakan tenant; tautan kapabilitas mengikat sumber daya, tindakan, dan masa kedaluwarsa; serta job diperiksa saat enqueue dan eksekusi. PostgreSQL RLS dapat menjadi jaring pengaman dari kesalahan, asalkan peran aplikasi tidak dapat melewatinya dan peralihan tenant pada pool koneksi telah diuji. Terakhir, saya akan mengganti ID di path, body, dan daftar massal pada matriks dua tenant dan multi-peran, lalu memastikan tidak ada respons, data, file, atau pesan yang melintasi batas tenant.”
Pembahasan Mendalam Langkah demi Langkah
Langkah 1: Inventarisasi tuple otorisasi dan titik masuk objek
Tulis aturan bisnis sebagai matriks sebelum menyebarkan nama-nama peran di seluruh controller:
| Relasi subjek | read | update | delete | share | export |
|---|---|---|---|---|---|
Tenant viewer, tidak dibagikan | Tolak | Tolak | Tolak | Tolak | Tolak |
Tenant viewer, dibagikan langsung | Izinkan | Tolak | Tolak | Tolak | Izinkan atau spesifik produk |
Tenant editor | Izinkan | Izinkan | Tolak | Spesifik kebijakan | Izinkan |
| Pemilik dokumen | Izinkan | Izinkan | Spesifik kebijakan | Izinkan | Izinkan |
Tenant admin | Izinkan | Izinkan | Izinkan | Izinkan | Izinkan |
Tabel ini merupakan asumsi skenario. Pemilik produk dan keamanan harus menyetujui matriks sebenarnya dan memberikan justifikasi untuk setiap aturan izinkan. Penolakan secara default (default deny) berarti tindakan baru, peran yang tidak dikenal, konteks tenant yang hilang, atau kesalahan kebijakan akan ditolak (fail closed).
Kemudian inventarisasi titik masuk objek: parameter path, filter kueri, body, larik massal, node GraphQL, token berbagi, kunci object storage, kunci cache, pesan, dan job. Definisi OWASP tidak mengharuskan ID berbentuk sekuensial. UUID, nama file, dan string generik juga merupakan referensi. Untuk setiap entri, catat sumber subjek, sumber tenant, tindakan, metode pencarian, dan efek samping akhir. Proses ini akan menyingkap potensi bypass.
Langkah 2: Dapatkan tenant tepercaya dari identitas terautentikasi
JWT yang terverifikasi memberikan user_id yang stabil; JWT tidak membuat setiap klaim peran berumur panjang menjadi selalu terkini atau membuat ID tenant yang dikirimkan secara terpisah otomatis tepercaya. Ketika pengguna memilih tenant_b, server mencari keanggotaan saat ini dan membuat AuthContext dengan cakupan permintaan (request-scoped):
AuthContext {
user_id,
tenant_id,
membership_id,
roles,
policy_version,
support_grant_id?
}Middleware memastikan konteks tersebut ada; kebijakan domain tetap menjadi penentu tindakan terhadap sumber daya. Connection pool, consumer, dan permintaan bersamaan (concurrent requests) tidak boleh berbagi tenant global yang dapat berubah (mutable). Tetapkan konteks basis data di dalam setiap transaksi dan bersihkan sebelum mengembalikan koneksi ke pool agar satu permintaan tidak mewarisi tenant dari permintaan sebelumnya.
Langkah 3: Masukkan cakupan tenant ke dalam kueri dan batasan data
Kueri yang tidak aman memuat data secara global:
SELECT * FROM documents WHERE id = :document_id;Batas dasar mencakup tenant tepercaya:
SELECT *
FROM documents
WHERE tenant_id = :auth_tenant_id
AND id = :document_id;Jika pembagian langsung menentukan visibilitas, lakukan join pada relasi pembagian di dalam kueri bercakup tersebut atau muat hanya sumber daya dari tenant yang sama lalu teruskan ke policy engine terpusat. Jangan pernah menyalin tenant_id dari body ke dalam predikat ini. Secara eksternal, hasil nol baris dapat secara konsisten menghasilkan respons 404 sehingga "ada di tenant lain" dan "tidak ada" tidak dapat dibedakan.
Desain skema membuat kesalahan lebih sulit terjadi. Tabel share, version, attachment, dan export-item semuanya memuat tenant_id. Jika sesuai, foreign key dan unique key menggunakan (tenant_id, resource_id) sehingga data turunan (child) di satu tenant tidak dapat merujuk ke data induk (parent) di tenant lain. Otorisasi aplikasi tetap diperlukan; batasan basis data (constraints) menolak relasi lintas tenant yang tidak disengaja pada saat penulisan data.
Langkah 4: Pertahankan otorisasi dan mutasi dalam satu batas kebenaran
Pola “Muat, otorisasi di kode aplikasi, perbarui nanti” memiliki celah time-of-check/time-of-use race condition. Izin atau status dokumen dapat berubah di antara langkah-langkah tersebut. Kebijakan sederhana dapat menggunakan pembaruan bersyarat:
UPDATE documents
SET title = :title, version = version + 1
WHERE tenant_id = :auth_tenant_id
AND id = :document_id
AND version = :expected_version
AND (
owner_id = :user_id
OR :can_edit_tenant_documents
);Jika nol baris terpengaruh, jangan publikasikan pesan, jangan tulis entri audit sukses, atau jangan perbarui cache. Pembagian yang kompleks dapat mengunci versi keanggotaan dan sumber daya yang relevan dalam satu transaksi, atau mengompilasi kebijakan ke dalam predikat basis data. Bukti otorisasi dan efek samping harus berbagi batas transaksi/versi yang eksplisit; snapshot lama tidak dapat mengotorisasi penulisan tanpa syarat di kemudian waktu.
Operasi massal membutuhkan kontrak atomisitas. Skenario ini menggunakan pendekatan all-or-nothing: normalisasi dan hapus duplikasi semua ID, lakukan kueri untuk kumpulan data yang terotorisasi di bawah satu tenant dan tindakan tepercaya, lalu tolak seluruh batch jika jumlahnya tidak cocok. Mengekspor subset yang terlihat secara diam-diam dapat menjadi existence oracle. Kontrak produk per-item juga dimungkinkan, tetapi harus mengotorisasi setiap objek dan menggunakan hasil yang tidak membocorkan informasi untuk item yang ditolak.
Langkah 5: Lindungi cache, unduhan, dan asynchronous job
Kunci cache setidaknya menyertakan tenant dan versi sumber daya, seperti document:{tenant_id}:{document_id}:{version}. Hit pada cache mengambil data; hal itu tidak melewati otorisasi tindakan saat ini. Jika keputusan otorisasi di-cache, kuncinya harus mencakup subjek, tenant, tindakan, sumber daya, versi relasi atau kebijakan, dan status pencabutan izin. Sistem yang kompleks sering kali lebih aman menyimpan data relasi di cache dan menghitung ulang keputusan otorisasi kecil tersebut.
URL unduhan presigned adalah kapabilitas berumur pendek. Setelah diterbitkan, pemegang URL dapat mengakses penyimpanan selama masa berlakunya. Periksa download sebelum menerbitkannya dan ikat sumber daya, tindakan, waktu kedaluwarsa, serta content disposition. Dokumen sensitif menggunakan masa berlaku singkat, unduhan sekali pakai atau melalui proksi, dan mekanisme pencabutan izin jika diperlukan. Tanda tangan membuktikan bahwa server yang menerbitkan URL tersebut; tanda tangan tidak memberikan izin kepada pemanggil yang tidak sah untuk mendapatkannya.
Proses ekspor diperiksa pada dua momen. API memverifikasi export pada setiap dokumen sebelum dimasukkan ke antrean. Saat eksekusi, worker menggunakan referensi subjek dan tenant di dalam job untuk memuat ulang keanggotaan dan izin sumber daya saat ini. Keanggotaan yang dicabut, perubahan tenant, atau izin dukungan teknis yang kedaluwarsa akan menghentikan job sebelum artefak yang dapat diunduh terbentuk. Payload job tidak boleh menerima flag admin dari pemanggil atau mempertahankan larik peran selamanya.
Langkah 6: Perlakukan PostgreSQL RLS sebagai pertahanan mendalam yang dapat diuji
Tabel bersama dapat mengaktifkan RLS sehingga kebijakan menyaring baris yang ada berdasarkan tenant transaksi tepercaya dan WITH CHECK membatasi baris yang disisipkan atau diperbarui. Penolakan default ketika tidak ada kebijakan yang cocok adalah mode kegagalan yang diharapkan. Sebelum rilis, verifikasi semua hal berikut:
- koneksi aplikasi bukan superuser, tidak memiliki atribut
BYPASSRLS, dan bukan pemilik tabel yang biasanya melewati kebijakan; gunakanFORCE ROW LEVEL SECURITYjika sesuai; - setiap transaksi menetapkan tenant dan membersihkannya sebelum dikembalikan ke pool; konteks yang hilang harus menolak akses alih-alih memilih tenant default;
USINGmencakup baris lama yang terlihat danWITH CHECKmencakup baris baru yang disisipkan atau diperbarui;- migrasi, backup, worker, dan alat dukungan teknis menggunakan peran terpisah dan prosedur eksplisit;
- kebijakan permisif digabungkan dengan
ORsecara default, sehingga penambahan kebijakan baru tidak sengaja memperluas akses; kebijakan dengan subkueri kompleks juga ditinjau terkait snapshot konkurensi dan biayanya.
RLS tidak dapat mengekspresikan setiap relasi produk dan tidak dapat melindungi object storage atau indeks pencarian yang melewati basis data. Kebijakan aplikasi memegang semantik bisnis penuh; RLS menghentikan kebocoran jika ada satu kueri yang melewatkan cakupan tenant. Uji kedua lapisan terhadap matriks izin yang sama demi konsistensi.
Langkah 7: Rancang penanganan kesalahan, audit, dan pengujian yang dapat dibuktikan salah (falsifiable)
Untuk objek lintas tenant atau tidak terlihat, skenario ini mengembalikan bentuk respons dan status 404 yang seragam. Penolakan tidak boleh mengembalikan judul, nama tenant, pemilik, versi, ukuran file, atau petunjuk perbedaan waktu respons (timing hint). Pembatasan laju (rate limiting) mengurangi enumerasi dan derau audit, tetapi klaim keamanan tidak boleh bergantung pada kegagalan penyerang dalam menebak UUID.
Audit pelaku (actor), tenant tepercaya, tindakan, representasi ID sumber daya yang terkontrol, keputusan, kode alasan, versi kebijakan, izin dukungan teknis, dan trace ID. Jangan mencatat konten dokumen, token kapabilitas unduhan, atau JWT lengkap ke log. Sinyal yang berguna mencakup penolakan lintas tenant, satu subjek yang memeriksa banyak objek yang hilang, kesalahan kebijakan, penolakan RLS, job yang dihentikan setelah pencabutan izin, dan penggunaan izin dukungan teknis.
Matriks pengujian setidaknya mencakup:
- dua tenant, masing-masing memiliki peran owner, viewer, editor, dan admin, ditambah pengguna yang izinnya telah dicabut dan operator dukungan teknis sementara;
- sumber daya milik sendiri, milik tenant yang sama dan tidak dibagikan, dibagikan langsung, UUID valid milik tenant lain, dan UUID yang tidak ada;
- list, GET, PATCH, DELETE, share, ekspor massal, GraphQL, unduhan, hit cache, dan eksekusi worker;
- substitusi ID pada path, kueri, JSON, larik, variabel GraphQL bertingkat, kunci penyimpanan, dan payload job;
- tidak ada kebocoran respons pada pembacaan dan tidak ada perubahan versi, status berbagi, file, pesan, indeks pencarian, atau log audit sukses pada penolakan;
- pencabutan izin dan pembaruan yang bersamaan, penggunaan satu koneksi pool oleh tenant secara berurutan, hilangnya konteks RLS, kesalahan konfigurasi peran aplikasi, dan cache otorisasi yang basi (stale).
Hasilkan kasus CI positif dan negatif dari matriks otorisasi, dan wajibkan setiap endpoint baru untuk mendaftarkan sumber daya dan tindakannya. Pemindai keamanan (scanner) dapat menemukan beberapa jalur yang dapat dienumerasi, tetapi mereka tidak memahami kepemilikan bisnis. Penggunaan banyak akun, objek eksternal valid yang diketahui, dan asersi efek samping adalah bukti penentu di sini.
Contoh Jawaban Berkualitas Tinggi
“Saya mengklasifikasikan kecacatan ini sebagai BOLA. JWT membuktikan identitas pemanggil, sementara API tidak memiliki izin tingkat tindakan untuk dokumen target. UUID mengurangi probabilitas enumerasi tetapi tidak mengubah keputusan otorisasi. Pertama, saya akan mendefinisikan matriks subject, tenant, action, resource, context dan menerapkan penolakan secara default (deny by default). Pengguna dapat memilih tenant aktif, tetapi server menurunkan konteks tenant dari keanggotaan aktif yang telah diverifikasi.
Lapisan data tidak lagi mengekspos findById global ke rute bisnis. Pembacaan pertama-tama dibatasi oleh tenant_id + document_id tepercaya, lalu menerapkan aturan peran, kepemilikan, dan pembagian untuk read, update, delete, share, atau export. Pembaruan sederhana menyertakan tenant, objek, kondisi tindakan, dan versi sumber daya dalam satu pernyataan bersyarat. Nol baris yang terpengaruh akan menghentikan setiap efek samping. Kebijakan yang kompleks menahan versi yang relevan dalam satu transaksi. Composite foreign key yang menyertakan tenant mencegah terjadinya relasi data turunan lintas tenant.
Saya akan memasukkan setiap potensi bypass ke dalam model tersebut. Ekspor massal mengotorisasi setiap ID dan bersifat all-or-nothing di sini. Kunci cache menyertakan tenant dan hit pada cache tetap melakukan otorisasi ulang. Tautan unduhan memerlukan izin download saat diterbitkan dan mengikat objek tersebut dengan masa kedaluwarsa singkat. Job diperiksa saat enqueue dan eksekusi sehingga pencabutan izin langsung berlaku sebelum ekspor dilakukan. RLS berfungsi sebagai jaring pengaman, tetapi peran aplikasi tidak boleh memiliki izin BYPASSRLS dan kepemilikan tabel, serta konteks transaksi pool harus diuji terhadap potensi kebocoran tenant.
Terakhir, saya akan menggunakan akun multi-peran di dua tenant dan menyisipkan satu UUID valid milik tenant lain ke dalam path, JSON, daftar massal, dan GraphQL. Pengujian mencakup jalur baca, tulis, hapus, ekspor, unduh, cache, dan worker. Setiap penolakan memastikan respons yang tidak membocorkan informasi dan tidak adanya perubahan versi basis data, file, pesan, atau indeks pencarian. Pendekatan ini membuktikan otorisasi tingkat objek jalur demi jalur sekaligus mencakup login, respons kesalahan, dan efek samping.”
Kesalahan Umum
- Menganggap UUID, Base64, atau ID terenkripsi sebagai perbaikan → ID dapat bocor melalui log, tautan berbagi, atau API lain, dan referensi yang valid tetap dapat menembus batas tenant → Otorisasi subjek, tenant, objek, dan tindakan pada setiap permintaan; gunakan ID acak hanya sebagai pertahanan mendalam.
- Mengizinkan ID apa pun setelah validasi JWT → Autentikasi hanya mengidentifikasi subjek tetapi tidak memberikan hak atas sumber daya → Bangun konteks tenant dari keanggotaan yang terverifikasi lalu buat keputusan atas objek.
- Memuat data secara global di controller dan menulis pemeriksaan pemilik secara manual → Rute baru, operasi massal, cache, dan worker akan melewatkannya, sementara pembagian tim salah ditolak → Sediakan akses data dengan cakupan tenant dan kebijakan terpusat dengan penolakan default.
- Hanya menguji metode GET → PATCH, DELETE, ekspor, GraphQL, dan unduhan mungkin masih membocorkan atau mengubah data → Hasilkan matriks uji negatif multi-akun di seluruh titik masuk dan tindakan.
- Menyaring ID yang ditolak dari batch yang berhasil → Jumlah dan isi data menjadi existence oracle, dan keberhasilan parsial menjadi ambigu → Tentukan semantik all-or-nothing atau per-item sejak awal dan otorisasi setiap objek.
- Mengasumsikan RLS otomatis aman → Pemilik tabel, superuser,
BYPASSRLS, hilangnya konteks, dan penggabungan kebijakan permisif dapat merusak isolasi → Verifikasi peran, konteks transaksi,USING/WITH CHECK, dan mode kegagalan. - Hanya melakukan asersi pada kode status 403 atau 404 → Sistem mungkin telah menulis data, mengirim pesan, atau membuat file sebelum penolakan terjadi → Pastikan setiap efek samping persisten dan eksternal tetap tidak berubah.
- Mencatat seluruh objek dan token ke log untuk investigasi → Telemetri keamanan justru menjadi sumber kebocoran data baru → Catat identitas minimal, tindakan, alasan, dan pengidentifikasi sumber daya yang terkontrol.
Pertanyaan Lanjutan dan Jawabannya
Pertanyaan Lanjutan 1: Jika UUIDv4 secara praktis tidak dapat ditebak, apakah otorisasi objek masih diperlukan?
Ya. Referensi dapat bocor melalui tautan berbagi, riwayat browser, log, notifikasi, analitik, API lain, atau pesan yang salah kirim. UUID hanya mengurangi enumerasi buta; UUID tidak menyatakan pemilik, tenant, tindakan, atau masa kedaluwarsa. Berikan akun penyerang satu UUID valid milik tenant lain dalam pengujian. Akses yang berhasil langsung membuktikan ketiadaan otorisasi.
Pertanyaan Lanjutan 2: Apakah mengembalikan 404 untuk setiap objek lintas tenant membuat proses debugging terlalu sulit?
Respons eksternal dapat dibuat seragam sementara audit internal tetap mencatat alasan yang stabil seperti RESOURCE_NOT_VISIBLE, ACTION_DENIED, atau TENANT_CONTEXT_INVALID. Operator melakukan investigasi melalui log yang terkontrol dan trace ID; pemanggil tidak mengetahui apakah suatu objek ada atau tidak. Jika kolaborasi dalam tenant yang sama memerlukan pesan "tidak ada izin mengedit", kembalikan 403 hanya setelah memastikan bahwa objek tersebut memang terlihat, di bawah kontrak API yang konsisten.
Pertanyaan Lanjutan 3: Apa yang terjadi pada antrean ekspor ketika seorang pengguna dihapus dari tenant?
Pengajuan yang berhasil tidak menciptakan hak baca permanen. Sebelum eksekusi, muat ulang keanggotaan dan izin export untuk setiap sumber daya. Setelah pencabutan, tolak job tersebut, hapus file sementara, dan jangan terbitkan URL unduhan. Jika kepatuhan regulasi mengharuskan hak akses dibekukan pada saat pengajuan, buat snapshot otorisasi eksplisit berumur pendek dengan cakupan, pemberi persetujuan, dan masa kedaluwarsa, alih-alih mempertahankan peran JWT yang sudah basi secara diam-diam.
Pertanyaan Lanjutan 4: Jika setiap kueri aplikasi sudah menyertakan tenant_id, apa nilai tambah dari RLS?
RLS dapat menghentikan kueri baru yang lupa menyertakan predikat tersebut dan membatasi beberapa jalur akses langsung ke basis data, sehingga memperkecil dampak radius kerusakan jika terjadi satu kelalaian. Namun, RLS juga menambah biaya pemeliharaan untuk konteks pool, peran, migrasi, dan kebijakan, serta tidak dapat melindungi pencarian, cache, atau object storage. Tabel bersama dengan tingkat sensitivitas tinggi adalah kandidat yang baik untuk menerapkan kedua lapisan tersebut. Pertama-tama, buktikan bahwa peran aplikasi tidak dapat melewati RLS, hilangnya konteks tenant akan menolak akses, konteks pool tidak bocor, dan kedua lapisan cocok dengan matriks yang sama.
Pertanyaan Lanjutan 5: Tim engineer dukungan teknis memerlukan akses sementara ke dokumen pelanggan. Bagaimana cara menghindari backdoor permanen?
Buat otorisasi dukungan teknis just-in-time terpisah yang terikat pada tiket, tenant target, tindakan yang diizinkan, pihak yang menyetujui, masa berlaku singkat, dan alasan. Tindakan sensitif dapat memerlukan persetujuan dua orang (two-person approval). Pisahkan endpoint dukungan teknis dari endpoint normal, tampilkan status sesi secara mencolok, larang pengunduhan massal, dan audit setiap akses objek. Berakhirnya masa berlaku akan langsung mencabut akses, dan penggunaannya ditinjau secara berkala. Peran aplikasi umum dan token layanan tidak boleh menerima izin lintas tenant.