Asosiy matnga o'tish
Blog

Veb

IDOR va avtorizatsiya xatolari: boshqa foydalanuvchi ma'lumotini ko'rish

URL'dagi raqamni o'zgartirib boshqa birovning hisobotini ochish mumkinmi? IDOR nima, uni qanday topish va Laravel'da policy bilan qanday yopish.

· 8 daq. o'qish

Foto: Vyacheslav Argenberg, CC BY 4.0 · Wikimedia Commons

Tasavvur qiling: foydalanuvchi o'z hisob-fakturasini /invoices/1042 manzilida ochadi. Keyin manzil satrida 1042 ni 1041 ga o'zgartiradi — va boshqa tashkilotning hisob-fakturasini ko'radi. Bu IDOR (Insecure Direct Object Reference) — obyektga to'g'ridan-to'g'ri, egalik tekshirilmasdan murojaat qilish.

IDOR OWASP Top 10 (2021) ro'yxatidagi birinchi o'rindagi toifa — A01 "Broken Access Control" ning eng keng tarqalgan ko'rinishi. CWE'da u CWE-639 (Authorization Bypass Through User-Controlled Key) sifatida tavsiflanadi. Uni avtomatik skaner deyarli topmaydi, chunki skaner qaysi yozuv kimga tegishli ekanini bilmaydi.

Autentifikatsiya va avtorizatsiya — har xil narsalar

Ko'p xatolarning ildizi shu ikki tushunchani aralashtirishda:

  • Autentifikatsiya — "sen kimsan?" Foydalanuvchi tizimga kirganmi.
  • Avtorizatsiya — "senga bu ruxsat etilganmi?" Aynan shu foydalanuvchi aynan shu yozuvni ko'rishi yoki o'zgartirishi mumkinmi.

auth middleware faqat birinchi savolga javob beradi. Tizimga kirgan har bir foydalanuvchi ikkinchi tekshiruvsiz har qanday ID bilan so'rov yubora oladi.

IDOR qayerlarda uchraydi

  • URL'dagi identifikatorlar: /orders/15, /users/7/edit, /reports/2026-09.pdf?id=88.
  • API so'rovlari: GET /api/documents/301, DELETE /api/comments/55.
  • Formalardagi yashirin maydonlar: <input type="hidden" name="user_id" value="7">.
  • Fayl yuklab olish: /download?file=contract_15.pdf.
  • Ko'p tashkilotli (multi-tenant) tizimlarda organization_id parametri.
  • To'lovdan keyingi return sahifasi: /payment/result?order=124.

Oxirgi holat ayniqsa ko'p e'tibordan chetda qoladi. To'lov oqimini to'g'ri qurish bo'yicha maqolamizda ham return sahifasi faqat buyurtma egasiga ochilishi kerakligini alohida ta'kidlaganmiz.

Laravel'dagi xato misoli

public function show($id)
{
    $invoice = Invoice::findOrFail($id);
    return Inertia::render('Invoices/Show', ['invoice' => $invoice]);
}

Marshrut auth middleware ichida bo'lishi mumkin, lekin bu kod yozuv kimga tegishli ekanini umuman tekshirmaydi.

To'g'ri yechim: policy

Laravel'da avtorizatsiya mantiqini bir joyda saqlash uchun policy ishlatiladi:

php artisan make:policy InvoicePolicy --model=Invoice
class InvoicePolicy
{
    public function view(User $user, Invoice $invoice): bool
    {
        return $invoice->organization_id === $user->current_organization_id;
    }

    public function update(User $user, Invoice $invoice): bool
    {
        return $this->view($user, $invoice)
            && $user->hasRole('accountant')
            && $invoice->status === 'draft';
    }
}

Kontrollerda:

public function show(Invoice $invoice)
{
    $this->authorize('view', $invoice);
    return Inertia::render('Invoices/Show', ['invoice' => $invoice]);
}

Yoki marshrut darajasida:

Route::get('/invoices/{invoice}', [InvoiceController::class, 'show'])
    ->can('view', 'invoice');

E'tibor bering: update metodida egalikdan tashqari rol va yozuv holati ham tekshirilyapti. Avtorizatsiya faqat "kimniki" degan savol emas, balki "bu holatda bu amalni bajarish mumkinmi" degan savol hamdir.

Yana bir qatlam: so'rovni egalik bilan cheklash

Ro'yxatlar va qidiruvlarda policy bilan har bir yozuvni alohida tekshirish o'rniga so'rovning o'zini cheklang:

$invoices = $request->user()->currentOrganization
    ->invoices()
    ->latest()
    ->paginate(20);

Bu holda boshqa tashkilotning yozuvi bazadan umuman olinmaydi. Bitta yozuv uchun ham shu usul ishlaydi: ->invoices()->findOrFail($id) begona ID uchun 404 qaytaradi.

Qoida: foydalanuvchidan kelgan har qanday identifikator — so'rov, xolos. Server har safar "bu yozuv shu foydalanuvchiga tegishlimi va bu amal ruxsat etilganmi?" deb o'zi tekshiradi.

Keng tarqalgan xatolar

  • Faqat interfeysda yashirish. Tugma yoki havola ko'rinmasa ham, so'rovni qo'lda yuborish mumkin.
  • Faqat o'qishni himoyalash. show tekshirilgan, update va destroy esa unutilgan.
  • Massiv yangilash. $invoice->update($request->all()) foydalanuvchiga organization_id yoki user_id maydonini o'zgartirishga imkon berishi mumkin. $fillable ro'yxatini aniq belgilang va validatsiyadan o'tgan ma'lumotni ishlating.
  • UUID'ga tayanish. Taxmin qilib bo'lmaydigan ID'lar yordam beradi, lekin ular avtorizatsiyaning o'rnini bosmaydi: ID boshqa joydan (log, havola, eksport) sizib chiqishi mumkin.
  • Bog'langan obyektlar. /projects/5/tasks/90 da loyiha tekshiriladi, lekin vazifa shu loyihaga tegishli ekani tekshirilmaydi.

Qanday tekshirish mumkin

Eng samarali usul — ikkita sinov akkaunti bilan qo'lda tekshirish:

  1. Ikkita foydalanuvchi yarating, iloji bo'lsa turli tashkilotlarda.
  2. Birinchi akkauntda yozuvlar yarating va ularning ID'larini yozib oling.
  3. Ikkinchi akkaunt bilan shu ID'larga GET, PUT, DELETE so'rovlarini yuboring.
  4. Har bir javob 403 yoki 404 bo'lishi kerak, 200 emas.
  5. Ruxsatlarni avtomatik testga aylantiring, shunda keyingi o'zgarishlar ularni buzmaydi:
it('boshqa tashkilot hisob-fakturasini ko\'rsatmaydi', function () {
    $invoice = Invoice::factory()->create();
    $stranger = User::factory()->create();

    $this->actingAs($stranger)
        ->get("/invoices/{$invoice->id}")
        ->assertForbidden();
});

Xulosa

IDOR texnik jihatdan oddiy, lekin topish uchun har bir marshrutni ko'rib chiqish va undagi egalik tekshiruvini izlash kerak. Bu ish katta loyihada ko'p vaqt oladi. Bizning jarayonda Claude AI agenti barcha marshrutlar, kontrollerlar va policy'larni tez ko'rib chiqib, tekshiruvsiz qolgan joylarni ajratadi, mutaxassis esa ularni ikkita akkaunt bilan amalda sinaydi va tuzatadi.

Loyihangizda boshqa foydalanuvchi ma'lumotiga yo'l ochiq qolmaganiga ishonch hosil qilmoqchi bo'lsangiz, skanerlash uchun ariza qoldiring. Biz avtorizatsiyadagi teshiklarni rasmiy ekspertizadan oldin yopishga yordam beramiz.