Belajar Livewire - Authorization & Security
Episode 13 of 23

Belajar Livewire - Authorization & Security

Mengamankan komponen Livewire: cek otorisasi dengan policy dan authorize() di setiap action, conditional rendering dengan @can, serta penanganan CSRF/XSS, sanitization, dan binding protection untuk mencegah manipulasi property dari client.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Sejauh ini kita membangun fitur tanpa memikirkan siapa yang berhak menjalankannya. Di aplikasi nyata — terutama admin panel dan ERP — pertanyaan "siapa yang boleh melakukan ini?" sama pentingnya dengan "bagaimana melakukannya?". Komponen Livewire yang interaktif membuat permukaan serangan berbeda dari halaman statis, jadi episode ini wajib dipahami sebelum produksi.

Poin penting yang sering diabaikan: menampilkan tombol bukan berarti user berhak menjalankan action-nya. Livewire mengirim request langsung ke server — user yang cerdas bisa memicu wire:click="delete(1)" tanpa melihat tombolnya. Karena itu semua otorisasi harus diperiksa di server, bukan hanya di tampilan.

Policy dan authorize()

Laravel menyediakan policy — class yang memusatkan aturan akses per model. Buat dengan artisan:

Buat policy
php artisan make:policy PostPolicy --model=Post
app/Policies/PostPolicy.php
public function update(User $user, Post $post)
{
    return $user->isAdmin() || $user->id === $post->user_id;
}
 
public function delete(User $user, Post $post)
{
    return $user->isAdmin();
}

Lalu di dalam action komponen, panggil authorize():

Authorize di action
use Livewire\Component;
 
public function update()
{
    $this->authorize('update', $this->post);
 
    // hanya user yang diizinkan sampai di sini...
    $this->form->update($this->post);
}
 
public function delete(int $postId)
{
    $post = Post::findOrFail($postId);
    $this->authorize('delete', $post);
 
    $post->delete();
}

Jika tidak berhak, authorize() melempar AuthorizationException (403) — dan action batal. Inilah lapisan pengaman utama: apapun yang terjadi di UI, server tetap menjadi hakim terakhir.

Warning

Jangan pernah mengandalkan tombol yang "tidak ditampilkan" sebagai pengaman. wire:click hanyalah permintaan HTTP — setiap user dengan akses ke halaman bisa memalsukannya. Pola yang benar: tampilkan tombol secara kondisional untuk UX, dan authorize() di action untuk keamanan.

Conditional Rendering dengan @can

Untuk UX, sembunyikan kontrol yang tidak berhak dipakai user:

Menampilkan aksi sesuai kemampuan
<div>
    <h1>{{ $post->title }}</h1>
 
    @can('update', $post)
        <button wire:click="edit">Edit</button>
    @endcan
 
    @can('delete', $post)
        <button wire:click="delete" wire:confirm="Yakin hapus?">Hapus</button>
    @endcan
</div>

Di dalam komponen Livewire, @can tetap bekerja normal karena user tersedia dari auth. Ingat: ini hanya lapisan tampilan — lapisan server (authorize) tetap wajib.

CSRF: Sudah Ditangani, Jangan Dimatikan

Livewire mengirim request via fetch dengan header khusus dan memvalidasi CSRF token Laravel di setiap request. Selama kalian tidak menonaktifkan middleware VerifyCsrfToken dan @livewireScripts tetap ada di layout, perlindungan CSRF berjalan otomatis:

  • Token CSRF disuntikkan oleh @livewireScripts ke header request.
  • Endpoint livewire/update memvalidasinya seperti route biasa.

Yang perlu kalian jaga: jangan buang @livewireScripts dari halaman hanya karena ingin menghemat beberapa kilobyte. Ia adalah bagian dari sistem keamanan, bukan sekadar pemuat JavaScript.

XSS: Output Escaping Otomatis

Blade secara default melakukan escaping pada output dengan {{ }}. Kalian yang menonaktifkannya:

Escaped (aman)
<p>{{ $post->title }}</p>
Tanpa escape (berbahaya jika berisi HTML user)
<p>{!! $post->body !!}</p>

Aturan emasnya:

  • {{ }} untuk data yang berasal dari user — sudah di-escape.
  • {!! !!} hanya untuk konten yang kalian kontrol penuh (misalnya hasil Markdown yang sudah di-sanitize), atau konten dari user yang sudah melewati filter ketat.

Untuk konten user berupa HTML, sanitize dulu di server (misalnya dengan htmlspecialchars, atau library sanitizer seperti HTMLPurifier) — jangan pernah menampilkan HTML mentah dari user langsung.

Sanitization Input

Bersihkan input sebelum diproses, bukan sesudah. Contoh: memastikan data string, membuang tag HTML, dan menetapkan default:

Sanitasi di action
public function saveComment()
{
    $this->validate([
        'body' => ['required', 'string', 'max:500'],
    ]);
 
    Comment::create([
        'post_id' => $this->postId,
        'body' => strip_tags($this->body),
    ]);
}

Aturan validasi (string, max) mencegah tipe data tak terduga; strip_tags membuang sisa markup dari field teks biasa. Sesuaikan sanitasi dengan konteks: field teks biasa = buang tag; editor kaya = sanitize whitelist HTML.

Binding Protection

Ini fitur keamanan spesifik Livewire yang wajib dipahami: karena public property di-isi dari payload client, secara teori user bisa memodifikasi property apa pun yang ter-serialisasi. Binding protection mencegah hal ini:

Property terkunci
use Livewire\Attributes\Locked;
 
class PostEditor extends Component
{
    #[Locked]
    public int $postId;
 
    public string $title = '';
}

#[Locked] membuat property tidak bisa diubah dari client — nilainya hanya bisa di-set dari server (mount()). Contoh di atas: user tidak bisa mengubah postId melalui request palsu untuk menyamar sebagai mengedit post lain.

Ada juga #[Session] untuk property yang dipertahankan di session server (tidak dikirim ke client sama sekali) — ideal untuk data sensitif seperti ID user aktif.

Pitfall Umum

  • Otorisasi hanya di tampilan: selalu pasangkan authorize() di action.
  • {!! !!} pada input user: XSS. Escape dengan {{ }} atau sanitize ketat.
  • Memakai ID dari payload tanpa #[Locked]: user bisa mengganti ID dan menyentuh data orang lain.
  • Menonaktifkan CSRF middleware: jangan, kecuali benar-benar paham konsekuensinya.
  • Tidak pernah mengecek otorisasi di mount(): data yang dimuat di awal pun harus dicek izinnya.

Penutup

Inti yang harus dibawa pulang:

  • Otorisasi wajib di server: authorize('ability', $model) di setiap action yang mengubah data.
  • @can hanya untuk UX; bukan pengaman.
  • CSRF ditangani Livewire otomatis; jaga @livewireScripts dan middleware tetap aktif.
  • Escape output dengan {{ }}; {!! !!} hanya untuk konten yang kalian kontrol.
  • Binding protection: #[Locked]/#[Session] untuk property yang tidak boleh diubah client.

Di episode 14 selanjutnya kita akan membahas lazy loading & wire:init — menunda render komponen berat dengan lazy/#[Lazy], placeholder states, dan strategi memotong payload halaman untuk waktu muat yang lebih cepat. Sampai jumpa di episode 14!

Belajar Livewire - Authorization & Security | Belajar Livewire