Mengirim instance Eloquent mentah ke dalam shared props Inertia.js adalah celah keamanan umum pada aplikasi monolit modern. Ketika objek model dioperasikan langsung di HandleInertiaRequests, seluruh atribut yang tidak masuk dalam $hidden akan diserialisasikan ke JSON dan tertanam di DOM publik via atribut data-page.
Vektor Kerentanan: data-page dan Serialisasi Mentah
Inertia.js bekerja dengan menyematkan payload awal pada elemen HTML saat first page load:
<div id="app" data-page="{"props":{"auth":{"user":{"id":1,...}}}}"></div>Jika middleware membagikan model User secara langsung via 'user' => $request->user(), risiko berikut muncul:
- Pelepasan Kolom Sensitif: Kolom seperti
two_factor_secret,stripe_id,internal_notes, atau flag permission internal dapat terekspos jika pengembang lupa mendaftarkannya ke array$hidden. - Relasi Terbuka (Eager Loading Leak): Relasi yang tidak sengaja dimuat di lifecycle request sebelumnya akan ikut terserialisasi ke dalam payload frontend.
- Mutasi
makeVisible(): Jika ada query di controller yang menjalankan$user->makeVisible('attribute'), atribut tersebut akan ikut bocor ke dalam shared props pada response yang sama.
Solusi Arsitektur: Whitelisting via DTO atau API Resource
Prinsip mitigasi utama adalah whitelisting, bukan blacklisting. Alih-alih mengandalkan proteksi pasif $hidden pada model Eloquent, transformasikan data secara eksplisit menggunakan API Resource atau array mapper.
Implementasi UserSharedResource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserSharedResource extends JsonResource
{
/**
* Transform the resource into an array.
* Hanya sertakan field yang benar-benar dibutuhkan oleh UI frontend.
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'roles' => $this->whenLoaded('roles', fn () => $this->roles->pluck('name')),
];
}
}
Evaluasi Lazy via Closure dan Partial Reloads
Shared props dievaluasi pada setiap request secara default, termasuk saat Inertia melakukan XHR request untuk navigasi internal. Untuk mencegah overhead database dan pembacaan state yang tidak perlu saat partial reload, bungkus data auth dalam Closure.
Inertia hanya akan mengeksekusi Closure jika prop tersebut diminta oleh client atau jika request bukan merupakan partial reload.
Konfigurasi HandleInertiaRequests
<?php
namespace App\Http\Middleware;
use App\Http\Resources\UserSharedResource;
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
protected $rootView = 'app';
public function version(Request $request): ?string
{
return parent::version($request);
}
public function share(Request $request): array
{
return array_merge(parent::share($request), [
'auth' => [
// ponytail: simple closure mapper, upgrade to dedicated DTO if role logic expands
'user' => fn () => $request->user()
? UserSharedResource::make($request->user())->resolve()
: null,
],
'flash' => [
'message' => fn () => $request->session()->get('message'),
'error' => fn () => $request->session()->get('error'),
],
// Pastikan token CSRF ditangani otomatis via cookie XSRF-TOKEN bawaan Laravel
]);
}
}
Catatan Partial Reload: Klien dapat memicu partial reload via header X-Inertia-Partial-Data. Penggunaan Closure memastikan query berat di shared props tidak dieksekusi saat client hanya meminta subset komponen halaman lain.Sesi dan Invalidasi State Otomatis
Ketika hak akses user berubah atau terjadi eskalasi sesi (misal: password reset atau logout dari sesi lain), frontend Inertia berisiko mempertahankan stale state jika user tidak melakukan full page refresh. Terapkan proteksi berikut:
- Rotasi Session ID: Pastikan
$request->session()->regenerate()dipanggil saat login. - Penanganan Status 419: Inertia otomatis mendeteksi response status HTTP
419 (Authentication Timeout / CSRF Mismatch)dan memicu full-window reload untuk memperbarui token dan props aplikasi secara utuh.
Verifikasi Keamanan via Automated Testing
Gunakan feature test untuk menjamin tidak ada regresi data pada shared props. Pustaka pengujian bawaan Laravel menyediakan helper assertions untuk payload Inertia.
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Inertia\Testing\AssertableInertia as Assert;
use Tests\TestCase;
class SharedPropsSecurityTest extends TestCase
{
use RefreshDatabase;
public function test_shared_user_prop_does_not_leak_sensitive_attributes(): void
{
$user = User::factory()->create([
'email' => '[email protected]',
'password' => bcrypt('super-secret-password'),
'remember_token' => 'secret-token-string',
]);
$this->actingAs($user)
->get('/dashboard')
->assertOk()
->assertInertia(fn (Assert $page) => $page
->has('auth.user', fn (Assert $prop) => $prop
->where('id', $user->id)
->where('email', '[email protected]')
->has('name')
// Validasi atribut sensitif tidak ada di payload JSON
->missing('password')
->missing('remember_token')
->missing('two_factor_secret')
->missing('two_factor_recovery_codes')
)
);
}
}
Jalankan test tersebut di pipeline CI/CD untuk memastikan setiap perubahan model tidak membocorkan data ke lapisan frontend tanpa disadari.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!