Insecure Direct Object References (IDOR) adalah kerentanan ketika sebuah endpoint mengembalikan objek hanya berdasarkan pengenal yang tertera pada permintaan (request), tanpa memeriksa apakah pemohon berhak atas objek tersebut. Topik ini membahas cara IDOR membuka eskalasi horizontal, mereproduksi kerentanan itu pada aplikasi faktur kecil dengan Python, lalu menutupnya menggunakan otorisasi berbasis objek. Setelah topik ini diikuti, pemeriksaan kepemilikan dapat ditempelkan pada setiap endpoint yang menerima pengenal objek.
Referensi objek langsung yang tidak diperiksa#
Banyak aplikasi web menyajikan data melalui alamat yang memuat pengenal objek secara langsung, misalnya /faktur/1042. Angka 1042 adalah referensi objek langsung, yaitu pengenal yang menunjuk persis satu baris data di server. Apabila server mengambil objek hanya dari pengenal itu tanpa memeriksa siapa pemohonnya, setiap pengguna yang sudah masuk dapat mengubah pengenal untuk membaca data milik pengguna lain. Kegagalan semacam ini termasuk kategori A01 Broken Access Control pada OWASP Top 10:2025, kategori yang menempati peringkat pertama.
Kontrol akses yang rusak dibedakan menjadi dua arah. Eskalasi horizontal terjadi ketika seorang pengguna mengakses data pengguna setara, misalnya budi membaca faktur milik citra. Eskalasi vertikal terjadi ketika seorang pengguna memperoleh hak istimewa yang lebih tinggi, misalnya pengguna biasa yang menjalankan fungsi admin. IDOR adalah wujud eskalasi horizontal yang paling umum karena pengenal objek biasanya mudah ditebak, misalnya nomor berurutan pada alamat.
IDOR tidak terbatas pada parameter id di jalur URL. Pengenal objek juga muncul pada isi JSON, parameter query, dan referensi bersarang di dalam respons, misalnya data faktur yang memuat customer_id milik pengguna lain. Seluruh lokasi itu memerlukan pemeriksaan yang sama karena bagi penyerang semuanya setara. Cara menemukannya juga sederhana, yaitu mengganti nilai pengenal dengan milik akun uji lain, kemudian melihat apakah responsnya berubah.
Simulasi eskalasi horizontal pada aplikasi faktur#
Aplikasi faktur berikut menyimpan dua faktur milik dua pengguna berbeda. Fungsi lihat_faktur_cacat() mengambil objek dari kamus FAKTUR hanya berdasarkan id, persis seperti handler yang lupa memanggil pemeriksaan otorisasi. Versi perbaikan membandingkan pemilik faktur dengan pemohon sebelum data dikembalikan. Simpan berkas berikut sebagai idor.py, lalu jalankan dengan perintah python3 idor.py.
"""Simulasi IDOR pada fitur lihat faktur.
Versi cacat mengembalikan faktur hanya berdasarkan id tanpa memeriksa
kepemilikan, sehingga budi dapat membaca faktur milik citra dengan
mengubah id pada permintaan. Versi perbaikan memeriksa pemilik faktur
terlebih dahulu sebelum mengembalikan datanya.
"""
FAKTUR = {
"1041": {"pemilik": "budi", "keterangan": "Tagihan langganan Maret", "nilai": 150000},
"1042": {"pemilik": "citra", "keterangan": "Tagihan langganan Maret", "nilai": 250000},
}
def lihat_faktur_cacat(pengguna, id_faktur):
"""Mengembalikan faktur apa pun tanpa memeriksa pemiliknya."""
faktur = FAKTUR.get(id_faktur)
if faktur is None:
return 404, "faktur tidak ditemukan"
return 200, faktur
def lihat_faktur_aman(pengguna, id_faktur):
"""Mengembalikan faktur hanya jika pemiliknya adalah pemohon."""
faktur = FAKTUR.get(id_faktur)
if faktur is None:
return 404, "faktur tidak ditemukan"
if faktur["pemilik"] != pengguna:
return 403, "akses ditolak: bukan pemilik faktur"
return 200, faktur
def jalankan(nama_versi, fungsi):
print(f"== {nama_versi} ==")
for pengguna, id_faktur in (("budi", "1041"), ("budi", "1042"), ("citra", "1042")):
status, isi = fungsi(pengguna, id_faktur)
print(f"{pengguna} melihat faktur {id_faktur} -> {status} {isi}")
jalankan("Versi cacat: tanpa pemeriksaan kepemilikan", lihat_faktur_cacat)
print()
jalankan("Versi perbaikan: pemeriksaan pemilik (deny by default)", lihat_faktur_aman)== Versi cacat: tanpa pemeriksaan kepemilikan ==
budi melihat faktur 1041 -> 200 {'pemilik': 'budi', 'keterangan': 'Tagihan langganan Maret', 'nilai': 150000}
budi melihat faktur 1042 -> 200 {'pemilik': 'citra', 'keterangan': 'Tagihan langganan Maret', 'nilai': 250000}
citra melihat faktur 1042 -> 200 {'pemilik': 'citra', 'keterangan': 'Tagihan langganan Maret', 'nilai': 250000}
== Versi perbaikan: pemeriksaan pemilik (deny by default) ==
budi melihat faktur 1041 -> 200 {'pemilik': 'budi', 'keterangan': 'Tagihan langganan Maret', 'nilai': 150000}
budi melihat faktur 1042 -> 403 akses ditolak: bukan pemilik faktur
citra melihat faktur 1042 -> 200 {'pemilik': 'citra', 'keterangan': 'Tagihan langganan Maret', 'nilai': 250000}Keluaran versi cacat memperlihatkan budi membaca faktur 1042 dan menerima data milik citra, lengkap dengan nilainya. Penyerang tidak memerlukan kata sandi citra sama sekali dan cukup mengubah satu angka pada permintaan. Nomor berurutan seperti 1041 dan 1042 mempermudah penemuan karena polanya dapat ditebak, tetapi perlu digarisbawahi bahwa pengenal yang sulit ditebak bukanlah perbaikan, melainkan sekadar penghambat. Pada versi perbaikan, permintaan yang sama berhenti dengan kode 403 sebelum data menyentuh respons (response).
Perbaikan: otorisasi berbasis objek#
Perbaikan utamanya adalah otorisasi berbasis objek (object-level authorization), yaitu pemeriksaan yang membandingkan pemilik objek dengan identitas pemohon pada setiap permintaan. Pemeriksaan ini berlaku deny by default, artinya akses ditolak kecuali kepemilikan terbukti, dan ditempatkan di sisi server. Penempatan pada sisi server bersifat mutlak karena menyembunyikan tautan di menu tidak mengubah apa pun bagi penyerang yang berbicara langsung dengan API. Pasang pemeriksaan tersebut di satu tempat yang selalu dijalankan, misalnya lapisan service atau middleware, agar endpoint baru tidak terlewat.
Pemohon pada simulasi diterima sebagai argumen, sedangkan pada aplikasi nyata identitas itu diambil dari session yang terikat token masuk, bukan dari parameter permintaan. Apabila pemohon boleh dikirim oleh klien, penyerang cukup mengaku sebagai pemilik objek dan seluruh pemeriksaan menjadi sia-sia. Pastikan nilai yang dibandingkan dengan pemilik objek berasal dari sumber yang tidak dapat diubah klien.
Jalankan demo berikut untuk mengalami keduanya. Masuk sebagai budi, buka /faktur/1042 milik citra dengan pemeriksaan nonaktif, lalu alihkan pemeriksaan ke aktif dan amati responsnya berubah menjadi 403.
Kesalahan umum#
Kesalahan berikut sering muncul ketika pemeriksaan kepemilikan dirancang.
- Memeriksa status masuk tetapi tidak kepemilikan. Autentikasi menjawab siapa pemohonnya, sedangkan otorisasi menjawab apa yang boleh diminta, sehingga keduanya tidak saling menggantikan.
- Menguji hanya melalui antarmuka. Tautan yang disembunyikan tetap dapat dipanggil langsung, sehingga pengujian perlu mengubah pengenal pada permintaan itu sendiri.
- Membiarkan endpoint selain pembacaan tanpa pemeriksaan. Endpoint pengubahan dan penghapusan justru lebih merusak apabila IDOR terjadi padanya.
- Kebingungan memilih 403 atau 404. Respons 404 menyembunyikan keberadaan objek dari pemetaan penyerang, sedangkan 403 lebih jelas untuk audit. Pilih salah satu dan terapkan secara konsisten.
Ringkasan#
- IDOR terjadi ketika endpoint mengembalikan objek hanya dari pengenalnya tanpa memeriksa pemohon, dan wujudnya adalah eskalasi horizontal.
- Perbaikannya adalah otorisasi berbasis objek yang menolak secara baku (deny by default) dan dijalankan di sisi server pada setiap permintaan.
- Pengenal acak seperti UUID hanya menghambat penemuan dan tidak menggantikan pemeriksaan kepemilikan.
Referensi#
- A01:2025 Broken Access Control — OWASP Top 10 — Kategori peringkat pertama OWASP Top 10:2025 yang menaungi kerentanan kontrol akses seperti IDOR.
- Access control vulnerabilities and privilege escalation — PortSwigger Web Security Academy — Materi kontrol akses aplikasi web yang membedakan eskalasi horizontal dan vertikal beserta contoh eksploitasinya.
- Direct Object References — OWASP — Definisi referensi objek langsung yang tidak aman beserta contoh dan pencegahannya.