Praktik Secure Coding (Part 6): Mencegah Privilege Escalation dengan RBAC di Next.js
Dalam dunia Cyber Security, celah kerentanan tidak berhenti setelah pengguna berhasil melewati halaman login. Serangan Privilege Escalation (Eskalasi Hak Akses) terjadi ketika seorang pengguna biasa mengeksploitasi sistem untuk mendapatkan kendali layaknya seorang Administrator.
Bagaimana jika dalam platform Learning Management System (LMS) yang kita bangun, seorang "Siswa" yang sudah berhasil login ternyata bisa memaksa masuk ke rute API dan menambahkan atau menghapus kelas matematika sesuka hati, padahal itu adalah wewenang mutlak seorang "Guru"?
Melanjutkan jurnal Part 5 (Halaman Login Kustom), kita akan menerapkan taktik Role-Based Access Control (RBAC). Taktik ini memastikan otorisasi berlapis di sisi Frontend maupun Backend menggunakan sesi token dari NextAuth. Mari kita bedah cara merajut lapis pertahanannya.
Mengapa Taktik RBAC Ini Lebih Aman?
- Kamuflase Antarmuka (UI Disguise): Komponen sensitif (seperti form tambah data) tidak hanya di-disable, melainkan benar-benar dihilangkan dari DOM browser jika peran pengguna tidak sesuai. Hacker tidak bisa memunculkannya melalui Inspect Element.
- Validasi Ganda (Double Verification): Mengunci antarmuka saja tidak cukup. API Backend kita juga akan dikonfigurasi untuk menolak permintaan POST dari token pengguna yang tidak memiliki wewenang (Guru/Admin), mencegah eksekusi melalui alat pihak ketiga seperti Postman.
- Keamanan Payload JWT: Peran pengguna (Role ID) yang kita ambil dari database MySQL disimpan secara aman di dalam token JWT yang dienkripsi oleh Secret Key NextAuth. Mustahil bagi peretas untuk memanipulasi nilainya melalui cookies.
Langkah Praktis: Membangun Otorisasi Berlapis
1. Otorisasi di Sisi Antarmuka (Frontend)
Buka file app/kelas/page.tsx yang kita buat di Part 3. Kita akan memperbarui kodenya untuk membaca sesi pengguna dan menyembunyikan form "Tambah Kelas" jika yang login adalah seorang Siswa.
Tambahkan pustaka NextAuth dan bungkus area form dengan logika kondisional:
'use client';
import { useState, useEffect } from 'react';
import { useSession } from 'next-auth/react'; // Tambahkan ini
export default function ManajemenKelas() {
const { data: session } = useSession(); // Membaca data token pengguna
const [daftarKelas, setDaftarKelas] = useState<any[]>([]);
const [name, setName] = useState('');
const [sortOrder, setSortOrder] = useState(0);
const [pesan, setPesan] = useState('');
// Anggap role_id '1' = Admin, '2' = Guru, '3' = Siswa
const userRole = (session?.user as any)?.role;
useEffect(() => {
fetchDataKelas();
}, []);
const fetchDataKelas = async () => {
// ... (Kode GET API sama seperti sebelumnya) ...
};
const handleSubmit = async (e: React.FormEvent) => {
// ... (Kode POST API sama seperti sebelumnya) ...
};
return (
<div style={{ padding: '30px', maxWidth: '600px', margin: '0 auto', fontFamily: 'sans-serif' }}>
<h1>Terminal Manajemen Kelas</h1>
{/* TAKTIK KEAMANAN: HANYA RENDER FORM JIKA ROLE ADALAH ADMIN ATAU GURU */}
{(userRole === '1' || userRole === '2') ? (
<div style={{ background: '#f9f9f9', padding: '20px', borderRadius: '8px', marginBottom: '30px', border: '1px solid #e2e8f0' }}>
<h3 style={{ marginTop: 0 }}>Tambah Entri Kelas (Mode Akses Khusus)</h3>
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
{/* ... (Input form sama seperti Part 3) ... */}
<button type="submit" style={{ padding: '12px', background: '#2b6cb0', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer', fontWeight: 'bold' }}>
Kirim Data
</button>
</form>
{pesan && <p style={{ marginTop: '15px', fontWeight: 'bold', color: '#2d3748' }}>{pesan}</p>}
</div>
) : (
<div style={{ background: '#fed7d7', padding: '15px', borderRadius: '8px', marginBottom: '30px', color: '#c53030', border: '1px solid #fc8181', textAlign: 'center' }}>
<strong>Akses Terbatas:</strong> Anda masuk sebagai Siswa. Anda hanya memiliki hak akses untuk melihat daftar kelas.
</div>
)}
{/* DAFTAR KELAS DARI DATABASE (Dapat dilihat oleh semua role) */}
<h3>Log Kelas Tersimpan</h3>
<ul style={{ listStyle: 'none', padding: 0 }}>
{daftarKelas.map((kelas) => (
<li key={kelas.id} style={{ padding: '12px', borderBottom: '1px solid #edf2f7', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span><strong>{kelas.name}</strong></span>
<span style={{ color: '#718096', fontSize: '0.9em' }}>Urutan: {kelas.sort_order}</span>
</li>
))}
</ul>
</div>
);
}
2. Pertahanan Absolut di Sisi Backend (API Route)
Menyembunyikan tombol dari antarmuka hanyalah ilusi optik bagi peretas mahir. Jika mereka menembak langsung ke endpoint /api/kelas melalui Thunder Client, data tetap bisa masuk. Oleh karena itu, kita harus mengunci API Backend kita!
Buka file app/api/kelas/route.ts (dari Part 2) dan tambahkan validasi sesi ini di dalam fungsi POST:
import { NextResponse } from 'next/server';
import { PrismaClient } from '@prisma/client';
import { getServerSession } from "next-auth/next"; // Pustaka pemeriksa sesi di server
const prisma = new PrismaClient();
export async function POST(request: Request) {
try {
// 1. CEK OTORISASI: Ambil sesi pengguna langsung dari mesin NextAuth
const session = await getServerSession();
// 2. VERIFIKASI HAK AKSES (Tolak jika tidak login atau jika dia adalah Siswa / role_id 3)
if (!session || (session.user as any).role === '3') {
return NextResponse.json(
{ status: 'error', message: '403 Forbidden: Anda tidak memiliki wewenang administrator!' },
{ status: 403 }
);
}
// 3. Jika lolos pengecekan keamanan, lanjutkan proses Insert ke database
const body = await request.json();
const { name, sort_order } = body;
// ... (Kode Prisma create sama seperti di Part 2) ...
} catch (error) {
return NextResponse.json(
{ status: 'error', message: 'Terjadi kesalahan server.' },
{ status: 500 }
);
}
}
Kesimpulan
Keamanan web (Web Security) menuntut pendekatan Zero Trust—jangan pernah berasumsi bahwa entitas yang telah berhasil login memiliki niat baik di dalam sistem. Dengan mengimplementasikan Role-Based Access Control (RBAC) pada lapis UI (React) dan lapis Server (API Routes), kita telah menutup celah ekskalasi hak akses secara paripurna.
LMS Anda kini bukan hanya sekadar bangunan yang terkunci, melainkan bangunan yang memiliki "Ruang VIP" yang hanya bisa dimasuki oleh entitas dengan kunci spesifik. Tetap waspada, dan selamat mengoding!
0 Response to "Praktik Secure Coding (Part 6): Mencegah Privilege Escalation dengan RBAC di Next.js"
Posting Komentar