Skip to content

[SECURITY] Prevent IDOR (Insecure Direct Object Reference) pada Endpoint Berbasis ID#978

Open
pandigresik wants to merge 2 commits intorilis-devfrom
dev-966
Open

[SECURITY] Prevent IDOR (Insecure Direct Object Reference) pada Endpoint Berbasis ID#978
pandigresik wants to merge 2 commits intorilis-devfrom
dev-966

Conversation

@pandigresik
Copy link
Contributor

Perbaikan issue #966

IDOR Prevention & Authorization Improvement Summary

Overview

Perbaikan keamanan untuk mencegah IDOR (Insecure Direct Object Reference) vulnerability, di mana user dapat mengakses, memodifikasi, atau menghapus data user lain hanya dengan menebak atau enumerasi ID.


Masalah Sebelumnya

Kondisi Awal

Aplikasi tidak memiliki object-level authorization. Controller hanya mengandalkan permission-based middleware tanpa memverifikasi apakah user yang login berhak mengakses resource dengan ID tertentu.

Contoh Kerentanan

// BEFORE: Tidak ada authorization check
public function edit($id)
{
    $user = User::find($id);
    // User dari kabupaten A bisa akses user dari kabupaten B
    // hanya dengan mengubah ID di URL
    return view('user.edit', compact('user'));
}

Dampak Keamanan

  • 🔓 Data Leak: User bisa melihat data sensitif user lain
  • ✏️ Data Tampering: User bisa mengedit data user lain
  • 🗑️ Data Destruction: User bisa menghapus data user lain
  • ⚠️ GDPR/Privacy Violation: Pelanggaran data pribadi

Solusi yang Diterapkan

1. Laravel Policy Implementation

Menggunakan Laravel Policy untuk object-level authorization:

// app/Policies/UserPolicy.php
public function view(User $user, User $model): bool
{
    // Administrator bisa melihat semua
    if ($user->hasRole('administrator')) {
        return true;
    }

    // User hanya bisa melihat diri sendiri
    if ($user->id === $model->id) {
        return true;
    }

    // Superadmin daerah bisa melihat user dengan kode_kabupaten yang sama
    if (
        $user->hasRole('superadmin_daerah') &&
        $user->kode_kabupaten &&
        $user->kode_kabupaten === $model->kode_kabupaten
    ) {
        return true;
    }

    return false;
}

2. Authorization Check di Controller

Menambahkan $this->authorize() di setiap method yang mengakses resource by ID:

// AFTER: Dengan authorization check
public function edit($id)
{
    $user = User::with('team')->where('id', $id)->first();
    
    // IDOR Prevention: Authorization check
    $this->authorize('update', $user);
    
    return view('user.edit', compact('user'));
}

File yang Dibuat/Dimodifikasi

1. Policy Classes (Baru)

app/Policies/UserPolicy.php

Authorization untuk User model:

  • viewAny() - Lihat daftar user
  • view() - Lihat detail user
  • create() - Buat user baru
  • update() - Update user
  • delete() - Hapus user
  • status() - Ubah status user

Rules:

Role View Update Delete Status
Administrator All All All (except superadmin) All (except superadmin)
Superadmin Daerah Same kabupaten Self
Kabupaten Same kabupaten (excl. admin) Self
Regular User Self Self

app/Policies/TeamPolicy.php

Authorization untuk Team/Group model:

Rules:

Role View Update Delete
Administrator All All All (except admin team)
Non-Admin Non-admin teams only Non-admin teams only

app/Policies/ArticlePolicy.php

Authorization untuk Article model:

Rules:

Action Permission Required
view website-article-read
update website-article-edit
delete website-article-delete
create website-article-create

2. Controller Updates

app/Http/Controllers/UserController.php

edit()      - Added $this->authorize('update', $user)
✅ profile()   - Added $this->authorize('view', $user)
✅ update()    - Added $this->authorize('update', $user)
✅ destroy()   - Added $this->authorize('delete', $user)
✅ status()    - Added $this->authorize('status', $user)

app/Http/Controllers/GroupController.php

edit() - Added $this->authorize('update', $team)

app/Http/Controllers/CMS/ArticleController.php

show()   - Added $this->authorize('view', $article)
✅ edit()   - Added $this->authorize('update', $article)
✅ update() - Added $this->authorize('update', $article)
✅ destroy()- Added $this->authorize('delete', $article)

3. AuthServiceProvider Update

app/Providers/AuthServiceProvider.php

protected $policies = [
    User::class => UserPolicy::class,
    Team::class => TeamPolicy::class,
    Article::class => ArticlePolicy::class,
];

4. Test Coverage (Baru)

tests/Feature/IdorPreventionTest.php

7 test cases untuk IDOR prevention:

✅ test_user_policy_prevents_unauthorized_access()
✅ test_users_with_same_kabupaten_can_access_each_other()
✅ test_users_edit_returns_403_for_unauthorized_user()
✅ test_users_update_returns_403_for_unauthorized_user()
✅ test_users_destroy_returns_403_for_unauthorized_user()
✅ test_groups_edit_returns_403_for_non_admin()
✅ test_user_policy_prevents_unauthorized_status_change()

Test Results:

PASS  Tests\Feature\IdorPreventionTest (7 tests)
✅ 8 assertions passed

Authorization Flow

┌─────────────────────────────────────────────────────────────┐
│                    Request: GET /users/5/edit               │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  UserController::edit($id = 5)                              │
│  $user = User::find(5);                                     │
│  $this->authorize('update', $user);  ← AUTHORIZATION CHECK  │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  UserPolicy::update($currentUser, $user)                    │
│  - Is admin? → ✅ Allow                                     │
│  - Is self? → ✅ Allow                                      │
│  - Same kabupaten? → ✅ Allow                               │
│  - Otherwise → ❌ Deny (403 Forbidden)                      │
└─────────────────────────────────────────────────────────────┘

Testing Checklist

Manual Testing

  • IDOR Test - Different Kabupaten

    # Login sebagai user kabupaten A
    # Coba akses user kabupaten B
    GET /users/edit/{id_user_kabupaten_b}

    Expected: 403 Forbidden

  • IDOR Test - Same Kabupaten

    # Login sebagai user kabupaten A
    # Coba akses user kabupaten A lain
    GET /users/edit/{id_user_kabupaten_a}

    Expected: 200 OK

  • IDOR Test - Admin Access

    # Login sebagai admin
    # Coba akses user manapun
    GET /users/edit/{any_id}

    Expected: 200 OK

  • IDOR Test - Self Access

    # Login sebagai user biasa
    # Coba akses diri sendiri
    GET /profile/edit/{self_id}

    Expected: 200 OK

Automated Testing

# Run IDOR prevention tests
php artisan test --filter IdorPreventionTest

# Run all tests to ensure no regression
php artisan test

Security Considerations

✅ Good Practices Implemented

  1. Object-Level Authorization: Setiap akses resource divalidasi
  2. Role-Based Access Control (RBAC): Permission berdasarkan role
  3. Data Isolation: User hanya bisa akses data di kabupaten sendiri
  4. Explicit Deny: Default deny, kecuali explicitly allowed
  5. Test Coverage: Automated test untuk IDOR prevention

⚠️ Attention Points

  1. Controller Coverage: Masih ada controller lain yang perlu authorization

    • SuplemenController
    • PointController
    • BantuanController
    • PendudukController
    • KeluargaController
    • dll.
  2. API Endpoints: API v1 juga perlu authorization check

  3. Eager Loading: Pastikan relasi juga di-check (misal: user.team)


Migration Guide

Untuk Developer

Jika membuat controller baru:

public function edit($id)
{
    $resource = Resource::findOrFail($id);
    
    // WAJIB: Authorization check
    $this->authorize('update', $resource);
    
    return view('...', compact('resource'));
}

Jika membuat policy baru:

php artisan make:policy ResourcePolicy --model=Resource

Daftarkan di AuthServiceProvider:

protected $policies = [
    Resource::class => ResourcePolicy::class,
];

Next Steps (Recommended)

Priority 1 - Critical Resources

  • PendudukController - Data penduduk sangat sensitif
  • KeluargaController - Data keluarga
  • BantuanController - Data bantuan sosial
  • SuplemenController - Data suplemen

Priority 2 - API Endpoints

  • Api/PermisionController
  • Api/TeamController
  • Api/IdentitasController

Priority 3 - CMS Resources

  • CMS/CategoryController
  • CMS/PageController
  • CMS/SlideController
  • CMS/DownloadController

References


Changelog

Version: 2026-03-09

Added:

  • UserPolicy - Authorization untuk User model
  • TeamPolicy - Authorization untuk Team model
  • ArticlePolicy - Authorization untuk Article model
  • IdorPreventionTest - Test coverage untuk IDOR prevention

Changed:

  • UserController - Authorization check di edit, profile, update, destroy, status
  • GroupController - Authorization check di edit
  • ArticleController - Authorization check di show, edit, update, destroy
  • AuthServiceProvider - Registered policies

Security:

  • ✅ IDOR prevention untuk User resource
  • ✅ IDOR prevention untuk Team resource
  • ✅ IDOR prevention untuk Article resource
  • ✅ Data isolation berdasarkan kabupaten
  • ✅ Role-based access control

@pandigresik pandigresik requested a review from vickyrolanda March 9, 2026 23:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant