Belajar CodeIgniter - Security Hardening (OWASP)
Episode 18 of 27

Belajar CodeIgniter - Security Hardening (OWASP)

Meninjau aplikasi dari kacamata OWASP Top 10: CSRF, XSS, SQL injection, keamanan file upload, dan security headers, lalu melakukan audit serta patch nyata pada aplikasi CodeIgniter.

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

Pendahuluan

Di episode 8 kita sudah mengenal CSRF, XSS, dan escaping. Di episode 17 kita belajar menguji kode. Sekarang saatnya menggabungkan semuanya dalam satu disiplin: security hardening berdasarkan OWASP Top 10.

Mengapa ini penting? Karena kerentanan bukan kekalahan "kalau" — melainkan "kapan". Penyerang memindai aplikasi publik secara otomatis, dan satu celah (SQL injection, XSS tersimpan, upload berbahaya) cukup untuk membobol sistem. Di episode 18 ini kita mengaudit aplikasi ci4-app kita satu per satu dan menambal celahnya.

1. SQL Injection (A03)

SQL Injection terjadi saat input user disisipkan langsung ke query SQL. Contoh berbahaya:

RENTAN - jangan pernah lakukan ini
$id = $this->request->getGet('id');
$query = $this->db->query("SELECT * FROM posts WHERE id = {$id}");

Jika id berisi 1 OR 1=1, seluruh tabel ter-expose. Pertahanan yang benar:

AMAN - Query Builder / parameterized query
// Query Builder meng-escape otomatis
$post = $this->db->table('posts')->where('id', $id)->get()->getRow();
 
// atau prepared statement dengan placeholder
$query = $this->db->query('SELECT * FROM posts WHERE id = ?', [$id]);

Warning

Aturan emasnya: jangan pernah membangun SQL dengan string concat dari input user. Selalu pakai Query Builder atau prepared statement dengan placeholder ?. Ini bukan saran — ini pembeda antara aplikasi aman dan aplikasi yang database-nya bisa di-dump penyerang.

2. XSS - Cross-Site Scripting (A03)

XSS terjadi saat input user dirender sebagai HTML. Di episode 8 kita sudah memakai esc(), tapi mari pastikan konsisten:

Audit escape output
<!-- AMAN -->
<h1><?= esc($post['title']) ?></h1>
<input value="<?= esc($post['title'], 'attr') ?>">
 
<!-- RENTAN - input dirender mentah -->
<h1><?= $post['title'] ?></h1>

Aturan audit: setiap output yang berasal dari user atau database harus melewati esc(). Untuk konten rich text yang perlu HTML, sanitasi dengan HTMLPurifier (episode 8) — jangan biarkan input mentah lewat.

3. CSRF - Cross-Site Request Forgery (A01)

Kita sudah mengaktifkan filter csrf global di episode 8. Audit:

  • Semua form memakai <?= csrf_field() ?>.
  • API endpoint memakai token auth (bukan CSRF form).
  • Pastikan request dengan method berbahaya (POST/PUT/DELETE) tidak bisa dijalankan tanpa token.
Cek filter CSRF aktif
// app/Config/Filters.php
public $globals = [
    'before' => [
        'csrf',
        'honeypot',
    ],
];

4. File Upload Security (A04)

Upload file adalah salah satu vektor paling berbahaya. Kode aman:

Upload file dengan validasi
$rules = [
    'avatar' => 'uploaded[avatar]|max_size[avatar,2048]|is_image[avatar]|mime_in[avatar,image/png,image/jpg,image/jpeg]',
];
 
if (! $this->validate($rules)) {
    return redirect()->back()->with('errors', $this->validator->getErrors());
}
 
$file = $this->request->getFile('avatar');
 
if ($file->isValid() && ! $file->hasMoved()) {
    $newName = $file->getRandomName();          // nama acak, bukan nama user
    $file->move(WRITEPATH . 'uploads', $newName); // di luar public/, dibahas di bawah
}

Poin keamanan:

  • getRandomName() — jangan pernah memakai nama file asli dari user (bisa berisi path traversal atau nama berbahaya).
  • Validasi mime & size — filter di level aplikasi sebelum file disimpan.
  • Simpan di luar public/ — file yang tidak perlu diakses publik (misal dokumen privat) sebaiknya di writable/uploads/; untuk file publik yang harus disajikan browser, beri nama acak dan kontrol akses.

5. Security Headers

Response headers yang benar memblokir banyak serangan client-side:

Set security headers via filter
namespace App\Filters;
 
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
 
class SecurityHeaders implements FilterInterface
{
    public function before(RequestInterface $request, $arguments = null): mixed
    {
        return null;
    }
 
    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null): ResponseInterface
    {
        return $response
            ->setHeader('X-Frame-Options', 'DENY')
            ->setHeader('X-Content-Type-Options', 'nosniff')
            ->setHeader('Referrer-Policy', 'no-referrer')
            ->setHeader('X-XSS-Protection', '0')
            ->setHeader('Content-Security-Policy', "default-src 'self'");
    }
}

Daftarkan di Config/Filters.php sebagai after global:

Filter headers global
public $globals = [
    'before' => ['csrf', 'honeypot'],
    'after'  => [
        'toolbar',
        'securityheaders' => [
            'except' => ['api/*'],
        ],
    ],
];

CodeIgniter 4 bahkan menyediakan SecureHeaders filter bawaan (di episode 19 kita akan melihat daftar filter lengkapnya) — tapi memahami isi header manual seperti di atas tetap penting.

6. Audit Aplikasi Kita

Audit cepat aplikasi blog ci4-app:

AreaPemeriksaanStatus
SQLSemua query lewat Query Builder/prepared?
XSSSemua output lewat esc()?
CSRFForm ada csrf_field(), filter aktif?
Passwordpassword_hash()/password_verify()?
UploadgetRandomName() + validasi mime?
HeadersSecurity headers terpasang?
AuthSession regenerate saat login?

Tip

Jadikan audit ini checklist berkala, bukan kegiatan sekali jalan. Banyak tim menaruhnya di Definition of Done: fitur "selesai" hanya jika lolos checklist keamanan di atas. Di episode 26 kita akan menyusun checklist lengkap akhir series.

Ringkasan Security Hardening (OWASP)

  • SQL Injection: Query Builder / prepared statement; jangan concat SQL.
  • XSS: esc() di semua output; HTMLPurifier untuk rich text.
  • CSRF: filter global + csrf_field() di setiap form.
  • Upload: getRandomName(), validasi mime/size, simpan di luar public/.
  • Headers: X-Frame-Options, X-Content-Type-Options, CSP, dll via filter.

Penutup

Inti yang harus dibawa pulang:

  • SQL Injection dicegah dengan tidak pernah concat SQL dari input user.
  • XSS dicegah dengan escape di output dan sanitasi di input.
  • File upload = validasi mime + nama acak + lokasi di luar public.
  • Security headers memblokir banyak serangan dengan konfigurasi sekali.

Di episode 19 selanjutnya kita akan membahas filter, middleware & rate limiting — filter bawaan, custom filter before/after, dan membangun throttler untuk melindungi API dan halaman login dari brute force. Keamanan kalian akan naik level!