Mass Assignment: Celah yang Sering Terlewat
Salah satu celah keamanan yang paling sering saya temui saat review kode Laravel — baik punya sendiri maupun punya rekan tim — bukan SQL injection atau XSS yang sudah jadi bahan hafalan semua developer. Justru mass assignment, celah yang terlihat sepele karena tidak butuh payload aneh-aneh, cukup menambahkan satu field ekstra di body request yang seharusnya tidak boleh diisi user.
Kenapa ini gampang lolos review? Karena kodenya terlihat "bersih". Tidak ada raw query, tidak ada string concatenation mencurigakan. Masalahnya justru di apa yang tidak ditulis: developer lupa membatasi field mana saja yang boleh diisi massal lewat create() atau update().
Apa sebenarnya yang terjadi
Di Laravel (dan framework lain dengan pola ORM serupa), method seperti Model::create($request->all()) akan mengisi semua kolom yang ada di array input ke kolom model yang bersangkutan, selama kolom itu tidak masuk daftar terlarang. Kalau developer tidak mendefinisikan $fillable atau $guarded dengan hati-hati, artinya siapa pun yang mengirim request bisa menambahkan field yang tidak dimaksudkan untuk diisi user biasa.
Contoh klasik: form registrasi user hanya menampilkan input name, email, dan password di frontend. Tapi kalau controller-nya menulis:
$user = User::create($request->all());
maka secara teknis, siapa saja yang mengirim request langsung ke endpoint (misalnya lewat curl atau Postman, bukan lewat form) bisa menyelipkan field tambahan seperti is_admin atau role. Kalau kolom itu ada di tabel users dan tidak dijaga di model, request itu akan berhasil mengubah dirinya sendiri jadi admin — tanpa exploit rumit, cukup menambah satu key di JSON body.
Kenapa ini gampang terlewat
Menurut pengalaman saya bekerja di sistem-sistem yang datanya sensitif (rekam medis, data pasien, resep), pola ini paling sering muncul di dua situasi. Pertama, saat developer ingin cepat, lalu memakai $request->all() sebagai jalan pintas dan berencana "nanti dibatasi belakangan" — yang biasanya tidak pernah benar-benar dikerjakan. Kedua, saat model lama sudah pakai $guarded = [] (artinya semua kolom fillable) karena dulu terasa praktis, lalu di kemudian hari ditambahkan kolom sensitif seperti role atau is_verified tanpa ada yang sadar bahwa kolom baru itu otomatis ikut ter-mass-assign.
Yang membuatnya lebih berbahaya di API dibanding form HTML biasa: form HTML membatasi apa yang bisa dikirim user lewat elemen input yang ditampilkan, tapi itu hanya pembatas di sisi tampilan. API tidak punya pembatas visual sama sekali — request body sepenuhnya bebas dibentuk oleh klien, jadi validasi dan whitelist di sisi server jadi satu-satunya garis pertahanan.
Cara menutup celah ini
Prinsip yang saya pegang: jangan pernah percaya bentuk request langsung masuk ke model tanpa filter eksplisit. Beberapa langkah praktis yang saya terapkan:
- Selalu definisikan
$fillablesecara eksplisit di model untuk kolom yang memang boleh diisi user, alih-alih mengandalkan$guarded = []. - Gunakan
$request->validated()dari Form Request class, bukan$request->all(), supaya hanya field yang lolos rule validasi yang diteruskan ke model. - Untuk field sensitif seperti role, status verifikasi, atau saldo, isi secara eksplisit dan terpisah dari proses mass assignment, misalnya lewat method tersendiri yang hanya bisa dipanggil dari jalur otorisasi tertentu (misalnya hanya dari panel admin yang sudah dicek permission-nya).
- Kalau pakai package admin panel seperti Filament, tetap periksa field mana yang terhubung ke form — kemudahan generate form otomatis kadang membuat developer lupa mengecek ulang field policy di baliknya.
Sebagai ilustrasi, bayangkan sebuah sistem dengan puluhan model dan ratusan kolom yang berkembang selama bertahun-tahun — celah semacam ini biasanya bukan muncul dari desain awal yang buruk, tapi dari akumulasi kolom baru yang ditambahkan tanpa peninjauan ulang terhadap kebijakan fillable/guarded yang lama.
Takeaway
Mass assignment bukan soal teknik exploit yang canggih, melainkan soal kedisiplinan: setiap kolom di model harus punya keputusan sadar apakah boleh diisi lewat request user atau tidak. Jangan andalkan bentuk form di frontend sebagai satu-satunya pembatas — validasi dan whitelist eksplisit di sisi server tetap wajib, terutama untuk API yang body request-nya bisa dibentuk bebas oleh siapa pun yang tahu endpoint-nya.