IDOR در API فاکتورها: دیدن و دانلود فاکتور هر کاربر دیگر با تغییر یک عدد
endpoint دانلود فاکتور فقط مالکیت رکورد را چک نمیکند؛ کافیست شناسهی URL را عوض کنی تا فاکتور هر کاربر دیگری را ببینی.
کد زیر (Laravel) یک route برای دانلود فاکتور کاربر است:
// routes/web.php
Route::get('/invoices/{id}/download', [InvoiceController::class, 'download'])
->middleware('auth');
// InvoiceController.php — نسخهی آسیبپذیر
public function download($id)
{
$invoice = Invoice::findOrFail($id); // <- هیچ چکی روی مالکیت نیست!
return response()->streamDownload(function () use ($invoice) {
echo $invoice->toPdf();
}, "invoice-{$invoice->id}.pdf");
}
شناسایی
با اکانت خودت یک فاکتور دانلود میکنی و آدرس را میبینی:
GET /invoices/104/download -> فاکتور خودت (آیدی 104)
فقط عدد را عوض میکنی:
GET /invoices/103/download -> فاکتور کاربر دیگری! (نام، مبلغ، آدرس، شاید شماره کارت بخشی)
چون middleware فقط چک میکند «لاگین هستی یا نه»، نه «این فاکتور مال خودت است یا نه»، با یک حلقهی ساده (مثلاً از 1 تا 10000) میشود کل فاکتورهای سایت را دانلود کرد.
تاثیر
افشای اطلاعات شخصی/مالی همهی کاربران (نقض GDPR-مانند)، و چون این الگو enumerable است (شناسهی عددی ترتیبی)، اتوماسیون آن بسیار ساده است.
راهحل
همیشه مالکیت را قبل از برگرداندن داده چک کن — بهترین راه در Laravel استفاده از Policy یا scope روی کاربر لاگینشده است:
public function download(Request $request, $id)
{
$invoice = $request->user()->invoices()->findOrFail($id);
// اگر فاکتور مال این کاربر نباشد، findOrFail خودش 404 میدهد
return response()->streamDownload(fn () => print($invoice->toPdf()), "invoice-{$invoice->id}.pdf");
}
یا با Laravel Policy که صریحتر و قابل تستتر است:
$this->authorize('view', $invoice); // InvoicePolicy::view چک میکند $invoice->user_id === $user->id
تمرین عملی در آزمایشگاههای IDOR (Insecure Direct Object Reference) ›