# Ketika RBAC Salah: Studi Kasus Keamanan untuk Full-Stack Developer

Beberapa waktu lalu muncul sebuah write-up bug bounty berjudul **“**[**How a Simple RBAC Mistake Led to a $20K+ Admin Takeover**](https://medium.com/@Seek404/how-a-simple-rbac-mistake-led-to-a-20k-admin-takeover-d196694791dd)**”** yang dipublikasikan oleh seorang security researcher. Kasus ini menunjukkan sesuatu yang sangat menarik: **kerentanan yang menyebabkan takeover sistem bukan berasal dari eksploitasi kompleks, melainkan dari kesalahan logika sederhana dalam kontrol akses.**

Kesalahan tersebut terjadi pada implementasi **Role-Based Access Control (RBAC)**. Backend hanya memvalidasi bahwa token pengguna valid, tetapi **tidak memverifikasi apakah pengguna tersebut memiliki hak akses yang sesuai**.

Akibatnya, pengguna biasa dapat memanggil endpoint admin secara langsung dan bahkan mengubah rolenya menjadi **Super Admin**.

Kasus ini memberikan pelajaran penting bagi para full-stack developer:

> **Authentication bukanlah Authorization.**

Login yang berhasil tidak berarti pengguna boleh melakukan semua aksi dalam sistem.

Dalam artikel ini kita akan membahas:

*   studi kasus kerentanan RBAC
    
*   contoh implementasi yang **buruk**
    
*   contoh implementasi yang **aman**
    
*   contoh **mini project API menggunakan Hono**
    

* * *

# Studi Kasus: Bagaimana Sistem Bisa Diambil Alih

Dalam kasus tersebut, sistem memiliki arsitektur seperti ini:

```plaintext
Mobile App
     │
     │
 Shared API Backend
     │
 ┌───────────────┐
 │ Admin Portal  │
 │ Invoice Portal│
 └───────────────┘
```

Mobile app menyediakan fitur **registrasi user**.

Setelah user mendaftar, sistem memberikan **token autentikasi**.

Masalah muncul karena backend hanya melakukan pemeriksaan:

```plaintext
token valid → allow request
```

tanpa memverifikasi role.

Peneliti keamanan kemudian mencoba mengakses endpoint admin menggunakan token user biasa:

```plaintext
GET /user/v2/users
GET /user/v2/acl
POST /user/v2/acl
```

Karena tidak ada validasi role, request tersebut berhasil.

Lebih buruk lagi, terdapat endpoint yang memungkinkan perubahan role:

```plaintext
POST /user/v2/acl
```

Dengan payload sederhana:

```json
{
  "role": "super_admin"
}
```

Pengguna biasa berhasil menaikkan hak aksesnya menjadi **Super Admin**.

Dari titik ini, ia dapat:

*   membaca seluruh data pengguna
    
*   mengubah role pengguna lain
    
*   mengontrol konfigurasi sistem
    
*   mengakses beberapa portal admin sekaligus
    

Kerentanan ini menghasilkan bug bounty lebih dari **$20.000**.

* * *

# Kesalahan Arsitektur yang Sering Terjadi

Beberapa pola kesalahan yang sering muncul pada aplikasi modern:

### 1\. Backend hanya memvalidasi token

Developer sering membuat middleware seperti:

```plaintext
if token valid:
   allow request
```

Padahal seharusnya:

```plaintext
if token valid AND role authorized:
   allow request
```

* * *

### 2\. Menganggap UI sebagai layer keamanan

Contoh kesalahan:

*   menu admin disembunyikan
    
*   tombol admin tidak muncul
    
*   route admin tidak ditampilkan
    

Namun endpoint API masih dapat dipanggil langsung menggunakan tools seperti:

*   Postman
    
*   Burp Suite
    
*   curl
    

* * *

### 3\. API dipakai oleh banyak aplikasi

Backend sering dipakai oleh:

*   mobile app
    
*   admin dashboard
    
*   internal tools
    

Jika RBAC tidak dirancang dengan baik, maka **token dari satu aplikasi bisa digunakan untuk mengakses sistem lain.**

* * *

# Contoh Project Buruk (Broken RBAC)

Berikut contoh mini API menggunakan **Hono** yang memiliki masalah yang sama seperti studi kasus tadi.

Struktur sederhana project:

```plaintext
src
 ├─ server.ts
 ├─ auth.ts
 └─ db.ts
```

* * *

## Middleware Authentication

```ts
import { verify } from "jsonwebtoken"

export const auth = async (c, next) => {
  const token = c.req.header("Authorization")

  if (!token) {
    return c.json({ error: "Unauthorized" }, 401)
  }

  try {
    const payload = verify(token, process.env.JWT_SECRET)
    c.set("user", payload)

    await next()
  } catch {
    return c.json({ error: "Invalid token" }, 401)
  }
}
```

Middleware ini hanya memastikan user login.

* * *

## Endpoint Admin (Tidak Aman)

```plaintext
GET /admin/users
```

Implementasi:

```ts
app.get("/admin/users", auth, async (c) => {
  return c.json(await db.users.findMany())
})
```

Masalahnya:

**tidak ada validasi role.**

Jika user biasa memiliki token valid, ia tetap bisa mengakses endpoint ini.

* * *

## Endpoint Role Update (Sangat Berbahaya)

```plaintext
POST /admin/role
```

Implementasi buruk:

```ts
app.post("/admin/role", auth, async (c) => {
  const body = await c.req.json()

  await db.users.update({
    where: { id: body.userId },
    data: { role: body.role }
  })

  return c.json({ success: true })
})
```

Attacker bisa mengirim request:

```json
{
 "userId": 15,
 "role": "super_admin"
}
```

Jika backend tidak memverifikasi role:

```plaintext
user → super_admin
```

Inilah **privilege escalation**.

* * *

# Versi Project yang Aman

Sekarang kita perbaiki implementasinya.

Kita pisahkan antara:

*   authentication
    
*   authorization
    

* * *

## Middleware Role Check

```ts
export const requireRole = (role) => {
  return async (c, next) => {
    const user = c.get("user")

    if (!user || user.role !== role) {
      return c.json({ error: "Forbidden" }, 403)
    }

    await next()
  }
}
```

Middleware ini memastikan hanya role tertentu yang boleh mengakses endpoint.

* * *

## Endpoint Admin Aman

```plaintext
GET /admin/users
```

Implementasi:

```ts
app.get(
  "/admin/users",
  auth,
  requireRole("admin"),
  async (c) => {
    return c.json(await db.users.findMany())
  }
)
```

Sekarang alurnya:

```plaintext
request
 ↓
auth middleware
 ↓
role validation
 ↓
endpoint handler
```

User biasa akan mendapatkan:

```plaintext
403 Forbidden
```

* * *

## Endpoint Role Update Aman

Endpoint ini sangat sensitif sehingga hanya boleh diakses oleh **super admin**.

```ts
app.post(
  "/admin/role",
  auth,
  requireRole("super_admin"),
  async (c) => {

    const body = await c.req.json()

    await db.users.update({
      where: { id: body.userId },
      data: { role: body.role }
    })

    return c.json({ success: true })
  }
)
```

Sekarang user biasa tidak dapat mengubah role.

* * *

# Praktik Terbaik untuk Developer

Beberapa prinsip penting ketika membangun API:

### 1\. Backend adalah sumber keamanan

Frontend tidak boleh dipercaya.

Semua kontrol akses harus dilakukan di backend.

* * *

### 2\. Setiap endpoint harus memiliki aturan akses

Contoh:

| Endpoint | Role |
| --- | --- |
| /users/me | user |
| /admin/users | admin |
| /admin/role | super\_admin |

* * *

### 3\. Gunakan prinsip Least Privilege

User hanya boleh melakukan hal yang benar-benar dibutuhkan.

* * *

### 4\. Uji API seperti hacker

Cobalah:

*   akses endpoint admin dengan user biasa
    
*   ubah parameter request
    
*   gunakan Postman untuk bypass UI
    

Tes sederhana ini sering menemukan bug besar.

* * *

# Kesimpulan

Kasus bug bounty tersebut menunjukkan satu hal yang sangat penting dalam pengembangan aplikasi modern.

Kerentanan terbesar sering kali bukan berasal dari eksploitasi kompleks, tetapi dari **kesalahan logika sederhana dalam kontrol akses**.

Kesalahan kecil seperti ini:

```plaintext
if token valid → allow
```

seharusnya ditulis:

```plaintext
if token valid AND role authorized → allow
```

Perbedaan kecil tersebut dapat menentukan apakah sistem Anda aman atau justru dapat diambil alih oleh attacker.

Sebagai full-stack developer, kita tidak hanya bertanggung jawab membuat aplikasi berjalan dengan baik, tetapi juga memastikan aplikasi tersebut **tidak mudah disalahgunakan**.

Karena dalam keamanan aplikasi, sering kali:

> **Bug paling berbahaya adalah bug yang terlihat paling sederhana.**

Sumber Inspirasi: [https://medium.com/@Seek404/how-a-simple-rbac-mistake-led-to-a-20k-admin-takeover-d196694791dd](https://medium.com/@Seek404/how-a-simple-rbac-mistake-led-to-a-20k-admin-takeover-d196694791dd)
