Belajar CodeIgniter - Form Validation & Security
Episode 8 of 27

Belajar CodeIgniter - Form Validation & Security

Melindungi aplikasi dari data input yang tidak valid: validation rules dan custom rules, menampilkan error dengan benar, lalu mempertahankan diri dari CSRF, XSS, dan input berbahaya dengan filter dan escaping bawaan.

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

Pendahuluan

Setelah di episode 7 skema blog kalian rapi dengan migrations dan seeding, sekarang tibalah saatnya mengamankan pintu masuk utama aplikasi: form. Setiap data yang dikirim user melewati form, dan tanpa validasi serta perlindungan keamanan, satu request jahat bisa merusak data, menjalankan script di browser orang lain, atau mengirim query aneh ke database.

Di episode 8 ini kita membahas form validation dan security dasar — dua hal yang di CodeIgniter 4 saling terkait: validation menolak data tidak valid, sedangkan CSRF/XSS protection memblokir serangan sebelum sampai ke aplikasi.

Validation Rules

CodeIgniter menyediakan sistem validasi via $this->validate() di controller:

Validasi di controller
public function create(): \CodeIgniter\HTTP\RedirectResponse | string
{
    $rules = [
        'title'  => 'required|min_length[3]|max_length[255]',
        'slug'   => 'required|alpha_dash|is_unique[posts.slug]',
        'status' => 'required|in_list[draft,published]',
    ];
 
    if (! $this->validate($rules)) {
        return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
    }
 
    $postModel = new \App\Models\PostModel();
    $postModel->save([
        'title'  => $this->request->getPost('title'),
        'slug'   => $this->request->getPost('slug'),
        'body'   => $this->request->getPost('body'),
        'status' => $this->request->getPost('status'),
    ]);
 
    return redirect()->to('/posts')->with('success', 'Post berhasil dibuat');
}

Rule digabung dengan |. Yang paling sering dipakai:

RuleFungsi
requiredNilai tidak boleh kosong
min_length[n] / max_length[n]Panjang string
valid_emailFormat email valid
alpha_dashHuruf, angka, underscore, dash
is_unique[posts.slug]Nilai unik di kolom tabel
in_list[a,b]Harus salah satu dari daftar
matches[field]Cocok dengan field lain (konfirmasi password)

withInput() mengembalikan data lama ke form sehingga user tidak perlu mengetik ulang, dan with('errors', ...) membawa array error ke session flash — keduanya kita ambil di view.

Menampilkan Error di View

app/Views/posts/create.php (cuplikan)
<?= $this->extend('layouts/default') ?>
 
<?= $this->section('content') ?>
    <?php if (session('errors')): ?>
        <div class="alert alert-danger">
            <ul>
                <?php foreach (session('errors') as $error): ?>
                    <li><?= esc($error) ?></li>
                <?php endforeach; ?>
            </ul>
        </div>
    <?php endif; ?>
 
    <?= form_open('posts/store') ?>
        <?= csrf_field() ?>
        <div>
            <label for="title">Judul</label>
            <input type="text" name="title" id="title"
                   value="<?= esc(old('title')) ?>">
        </div>
        <button type="submit">Simpan</button>
    <?= form_close() ?>
<?= $this->endSection() ?>

old('title') mengembalikan nilai yang dikirim sebelumnya (dari withInput()), dan csrf_field() menyisipkan token CSRF — kita bahas sebentar lagi.

Tip

redirect()->back() kembali ke halaman asal, tapi jika ada masalah dengan referer, gunakan redirect eksplisit: redirect()->to('/posts/create'). Untuk form yang di-submit via AJAX, kembalikan JSON error dan render di client.

Custom Validation Rules

Rule bawaan tidak selalu cukup. Contoh: validasi slug yang sudah dipakai. Dua cara membuat rule custom:

Cara 1 — callable anonim:

Custom rule via closure
$rules = [
    'slug' => static function (string $value): bool {
        return (bool) preg_match('/^[a-z0-9-]+$/', $value);
    },
];

Cara 2 — class rules yang terdaftar:

Buat app/Validation/MyRules.php dan daftarkan di app/Config/Validation.php $ruleSets:

app/Validation/MyRules.php
<?php
 
namespace App\Validation;
 
class MyRules
{
    public function slug_valid(string $str): bool
    {
        return (bool) preg_match('/^[a-z0-9-]+$/', $str);
    }
}

Lalu gunakan sebagai slug_valid di rule:

Menggunakan custom rule
$rules = ['slug' => 'required|slug_valid'];

CSRF Protection

CSRF (Cross-Site Request Forgery) adalah serangan di mana penyerang mengelabui browser korban untuk mengirim request (misal: ganti password) ke aplikasi yang sedang login. Pertahanannya: setiap form membawa token unik yang dicek server.

Di CodeIgniter 4, filter CSRF sudah terdaftar global untuk semua route POST. Aktifkan di app/Config/Filters.php:

app/Config/Filters.php
public $globals = [
    'before' => [
        'csrf',
        'honeypot',
    ],
];

Lalu setiap form harus menyertakan token — itulah csrf_field() yang kita pakai di view. Saat submit, server membandingkan token; jika tidak cocok, request ditolak dengan 403.

Form dengan token CSRF
<?= form_open('posts/store') ?>
    <?= csrf_field() ?>
    ...
<?= form_close() ?>

Warning

Jika kalian menguji form dengan curl/Postman dan mendapat error CSRF, itu normal — CSRF token adalah proteksi nyata, bukan gangguan. Untuk API endpoint (episode 13), filter CSRF sengaja dikecualikan karena digantikan API key/token auth.

XSS Filtering dan Input Escaping

XSS (Cross-Site Scripting) terjadi saat input user dirender sebagai kode oleh browser korban. Dua lapis pertahanan:

1. Escape saat Output

esc() mengubah karakter berbahaya (<, >, &, ", ') menjadi entitas HTML:

Escape semua output
<h1><?= esc($post['title']) ?></h1>
<p><?= esc($post['body']) ?></p>

2. Sanitasi saat Input (untuk konten berformat)

Untuk konten yang memang perlu menyimpan HTML (misalnya editor rich text), gunakan sanitizer. CodeIgniter menyediakan HTMLPurifier melalui library:

Install HTMLPurifier
composer require ezyang/htmlpurifier
Sanitasi konten HTML
use HTMLPurifier;
use HTMLPurifier_Config;
 
$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
$cleanBody = $purifier->purify($this->request->getPost('body'));

Aturan sederhananya: escape semua output secara default; jika memang harus mengizinkan HTML, sanitasi dengan whitelist tag di input.

Note

Aturan emasnya: validasi menolak data buruk di input, escaping memastikan data apapun aman di output. Keduanya wajib — validasi tanpa escaping bisa bocor lewat data lama, escaping tanpa validasi membiarkan data sampah masuk database.

Ringkasan Form Validation & Security

  • $this->validate($rules) di controller, error dibawa dengan withInput() dan with('errors').
  • Custom rules via closure atau class yang didaftarkan di Validation.php.
  • Filter CSRF melindungi semua POST; setiap form wajib csrf_field().
  • Escape output dengan esc(), sanitasi HTML dengan HTMLPurifier jika memang dibutuhkan.
  • old() dan withInput() membuat form tetap ramah saat validasi gagal.

Penutup

Inti yang harus dibawa pulang:

  • Validasi dilakukan di server — jangan percaya validasi client-side saja.
  • is_unique, in_list, dan alpha_dash mencegah data aneh masuk database.
  • Aktifkan filter CSRF global dan selalu sertakan token di form.
  • Escape di output, sanitasi di input — dua lapis anti-XSS.

Di episode 9 selanjutnya kita akan membahas sessions, flash data & cookies — menyimpan state antar request, memanfaatkan flashdata untuk pesan sekali tampil, mengelola cookie, dan praktik login/logout sederhana. Persiapan untuk autentikasi penuh di episode 12!

Belajar CodeIgniter - Form Validation & Security | Belajar CodeIgniter