Skip to content
endr
Go back

IDOR: Ganti Angka di URL, Buka Profil Orang Lain

ID

IDOR itu bug akses yang eksploitasinya kadang cuma ganti satu angka. Aplikasi nerima ID object dari user, langsung dipake buat ngambil data, tanpa ngecek dulu user itu emang berhak apa enggak. Hasilnya lo bisa baca punya orang lain.

Gua ketemu contoh yang gamblang banget pas ngerjain exam bootcamp cyber security, target yang sama tempat gua nemu race condition di redeem voucher. Bedanya kali ini gua enggak buka Burp sama sekali. Cuma modal browser sama halaman profil.

Login pake akun test, username kipli. Buka /user/profile.php, ya wajar nongol data kipli sendiri: email kipli@skibidi.io, nomor telepon, alamat. Customer ID-nya #30, muncul di kartu Account Info sebelah kiri.

Halaman profil akun kipli, customer ID #30, data email/telepon/alamat milik sendiri (domain di-blur)

URL-nya https://exam.redacted/user/profile.php. Enggak ada parameter apa-apa. Tapi pas gua lihat request lain di aplikasi ini (halaman order, halaman edit), pola URL-nya sering bawa id di query string. Jadi gua coba iseng nambahin ?user_id=3 di belakang URL profil.

Kepencet enter, dan halamannya ganti jadi punya jane.smith. Bukan punya kipli lagi.

Halaman profil berubah jadi milik jane.smith cuma dengan nambahin ?user_id=3 di URL, termasuk nomor kartu kredit yang muncul (domain di-blur)

Nama, email jane.smith@yahoo.com, nomor HP, alamat lengkap Jl. Thamrin No.10, Surabaya, sampai nomor kartu kredit. Field itu di form sendiri judulnya Credit Card (Secure?), disabled dan di-mask, tanda tanyanya sendiri udah curiga. Di endpoint ini malah kebaca penuh: 3782-4567-8901-2345. Gua diem sebentar lihat itu, bukan karena susah nemuinnya, tapi karena gampang banget: ganti satu angka doang, langsung dapet PII sama data kartu punya orang lain.

Enggak ada tool, enggak ada script, enggak ada race window yang harus dikejar kayak kasus voucher kemarin. Login, ganti angka di address bar, terus enter, kelar.

Ini bukan race condition, ini otorisasi yang ilang

Beda sama race condition, bug ini bukan soal timing. Ini CWE-639, Authorization Bypass Through User-Controlled Key: server percaya aja ID yang dikirim dari parameter request, tanpa ngecek apakah user yang login emang pemilik ID itu (CWE-639). Nama umumnya IDOR, Insecure Direct Object Reference: aplikasi expose referensi langsung ke object internal (di sini: user_id), dan enggak ada access control check yang mastiin ID itu emang punya si pemanggil (PortSwigger, Insecure direct object references).

OWASP API Security Top 10 nempatin ini di posisi pertama, Broken Object Level Authorization: API endpoint yang nerima ID object dari user tapi enggak validasi level akses ke object itu (OWASP API Security Top 10 2023, API1). Ilustrasi umum kodenya:

-- ilustrasi alur rentan
SELECT email, phone, address, credit_card
FROM users
WHERE id = $1;  -- $1 langsung dari query param user_id, bukan dari session

Enggak ada AND id = session.user_id di situ. Query-nya sah secara SQL, cuma nanya “kasih data user dengan ID segini”, dan server enggak peduli siapa yang nanya.

Gua sempat mikir ini termasuk access control problem yang “boring” dibanding race condition, secara enggak butuh Turbo Intruder atau gate mechanism apa pun. Tapi justru itu yang bikin ngeri: exploitability-nya jauh lebih rendah. Race condition butuh alat, timing, kadang beberapa kali coba sampai kena window-nya. IDOR modalnya jari sama nomor urut.

Order page kena pola yang sama

Pas gua cek endpoint order (order_id di query string), polanya identik: ambil ID dari parameter, query langsung, enggak ada pengecekan baris itu punya siapa. Enggak gua screenshot detail di sini, tapi dari behaviour-nya konsisten sama profil, naikin/turunin angka, dapet order orang lain.

Dua-duanya nunjukin akar masalah yang sama: object-level authorization check-nya enggak dipasang di layer yang bener, entah itu enggak ada sama sekali atau cuma dicek pas render UI doang (padahal endpoint-nya sendiri masih bisa diakses langsung).

Cara nutupnya

Yang paling penting: jangan percaya ID dari client buat nentuin baris mana yang boleh dibaca. Untuk endpoint “profil gua sendiri”, idealnya ID target enggak usah diterima dari parameter sama sekali, ambil dari session:

-- ilustrasi fix, ID target dari session, bukan dari parameter
SELECT email, phone, address, credit_card
FROM users
WHERE id = $1;  -- $1 = current_session.user_id, hasil decode token/session

Kalau memang butuh nerima ID dari client (misal admin buka profil user lain), ownership atau role-nya harus divalidasi eksplisit sebelum query jalan:

SELECT email, phone, address, credit_card
FROM users
WHERE id = $1 AND (id = $2 OR $3 = 'admin');
-- $2 = session.user_id, $3 = session.role

Beberapa lapis tambahan yang layak dipasang bareng:

Kartu kredit yang field-nya udah dikasih label “(Secure?)” tapi tetep kebaca lewat endpoint lain itu pengingat juga: masking di UI doang enggak ngamanin apa-apa kalau API di baliknya masih ngasih data mentah ke siapa pun yang nanya duluan.

Studi kasus ini gua dapetin di exam bootcamp cyber security yang emang dipakai buat belajar :D.


Sumber:


Share this post:

Previous Post
Race Condition dan TOCTOU: Kenapa Cek Dulu, Baru Update, Itu Bahaya
Next Post
RCE via LFI: Ganti Parameter page, Jalanin Command di Server