31 Mar 2026 • Admin KhalimZone

Security Best Practices di CI4

Home / Blog / Security Best Practices di CodeIgniter 4
CodeIgniter 4 Security Best Practices

Security Best Practices di CI4 — Proteksi Aplikasi Dari Serangan

Khalim Security & Defense 14 menit baca

Security adalah bukan opsional — ini mandatory. Gue udah liat banyak aplikasi CI4 yang dibikin tanpa mikir security, hasilnya vulnerable dari berbagai jenis attack. SQL injection, XSS, CSRF, password cracking — semuanya bisa terjadi.

Di artikel ini, gue bakal kasih praktik security yang paling penting dan sering terlewat. Ini bukan teori, ini best practices dari real-world production apps.

Security Practices
01 Prevent SQL Injection dengan Query Builder

SQL injection adalah vulnerability paling berbahaya. Jangan pernah concatenate user input langsung ke SQL query.

php
// ❌ SANGAT DANGEROUS — SQL Injection! $email = $this->request->getVar('email'); $user = $this->db->query("SELECT * FROM users WHERE email = '$email'"); // ✅ BAGUS — gunakan query builder $email = $this->request->getVar('email'); $user = $this->db->table('users') ->where('email', $email) ->get() ->getRow();

Query builder otomatis escape dan sanitize input. Atau kalau pakai raw query, selalu gunakan placeholders:

php
// ✅ Raw query dengan binding (secure) $email = $this->request->getVar('email'); $user = $this->db->query( "SELECT * FROM users WHERE email = ?", [$email] );
⚠️

CI4 query builder sudah protect dari SQL injection. Tapi selalu validate & sanitize input di application level juga.

02 Prevent XSS (Cross-Site Scripting)

XSS terjadi ketika attacker inject malicious script yang dijalankan di browser user. Selalu escape output!

php
// ❌ DANGEROUS — XSS vulnerability <h1><?= $userInput ?></h1> // ✅ BAGUS — escape dengan esc() <h1><?= esc($userInput) ?></h1> // Atau spesifik context-nya: <h1><?= esc($userInput, 'html') ?></h1> <img src="<?= esc($url, 'attr') ?>"> <script>var name = "<?= esc($name, 'js') ?>";</script>

Di template, gunakan double curly braces:

blade
<!-- ❌ DANGEROUS --> <h1>{!! $userInput !!}</h1> <!-- ✅ BAGUS -- auto-escaped --> <h1>{{ $userInput }}</h1>
💡

Gunakan {! !} hanya untuk trusted content. Jangan untuk user input!

03 Protect dari CSRF (Cross-Site Request Forgery)

CSRF memaksa user melakukan action tanpa mereka tahu. CI4 sudah built-in protection, cuma perlu enable.

blade
<!-- ❌ DANGEROUS -- tanpa token --> <form method="POST" action="/update-profile"> <input type="text" name="name"> <button type="submit">Update</button> </form> <!-- ✅ BAGUS -- dengan token --> <form method="POST" action="/update-profile"> <?= csrf_field() ?> <input type="text" name="name"> <button type="submit">Update</button> </form>

Untuk AJAX request, add token ke header:

javascript
const csrfToken = document.querySelector('meta[name="csrf-token"]').content; fetch('/update-profile', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, }, body: JSON.stringify({ name: 'John' }) });
⚠️

CSRF protection auto-check token. Invalid token = 403 error.

04 Hash Password dengan password_hash()

Jangan simpan password plain text atau hash dengan MD5/SHA1. Pakai password_hash().

php
// ❌ DANGEROUS $password = md5($userInput); // ✅ BAGUS -- password_hash() $hashedPassword = password_hash($userInput, PASSWORD_BCRYPT, [ 'cost' => 12, ]);

Saat verifikasi login:

php
public function login() { $email = $this->request->getVar('email'); $password = $this->request->getVar('password'); $user = $this->userModel ->where('email', $email) ->first(); if (!$user || !password_verify($password, $user['password'])) { return redirect()->back(); } session()->set('user_id', $user['id']); }
💡

Gunakan password_verify() untuk cek, bukan string comparison.

05 Input Validation & Sanitization

Jangan percaya input user. Selalu validate di backend (frontend validation mudah di-bypass).

php
public function register() { if (!$this->validate([ 'name' => 'required|string|min_length[3]', 'email' => 'required|valid_email|is_unique[users.email]', 'password' => 'required|min_length[8]', ])) { return redirect()->back() ->withInput() ->with('errors', $this->validator->getErrors()); } $data = $this->getValidatedInput(); $this->userModel->insert($data); }
💡

CI4 validation rules powerful — baca docs untuk lengkap list rules.

06 Secure API dengan HTTPS & API Keys

Jangan transmit sensitive data di HTTP. Selalu pakai HTTPS di production.

php
// app/Config/App.php public $baseURL = 'https://example.com'; public $forceGlobalSecureRequests = true;

Untuk API, pakai API Keys atau JWT:

php
// app/Filters/ApiKeyFilter.php class ApiKeyFilter implements FilterInterface { public function before($request, $arguments = null) { $apiKey = $request->getHeaderLine('X-API-Key'); if (!$apiKey || !$this->isValidApiKey($apiKey)) { return response([ 'error' => 'Invalid API key', ], 401); } } private function isValidApiKey($key): bool { return hash_equals( getenv('API_KEY'), $key ); } }
💡

Gunakan hash_equals() untuk timing-safe comparison.

07 Limit File Upload & Validate Type

File upload adalah attack vector. Validate file size, type, dan extension.

php
public function uploadAvatar() { if (!$this->validate([ 'avatar' => 'uploaded[avatar]|max_size[avatar,2048]|is_image[avatar]', ])) { return redirect()->back(); } $file = $this->request->getFile('avatar'); $newName = $file->getRandomName(); // Move ke folder aman $file->move('/var/uploads', $newName); $this->userModel->update(session()->get('user_id'), [ 'avatar' => $newName, ]); }
⚠️

Jangan simpan file di public/ folder. Simpan di folder lain dan serve via controller.

08 HTTP Security Headers

HTTP headers block beberapa attack di browser level.

php
// app/Filters/SecurityHeadersFilter.php class SecurityHeadersFilter implements FilterInterface { public function after($request, $response, $arguments = null) { $response->setHeader('X-Frame-Options', 'DENY'); $response->setHeader('X-Content-Type-Options', 'nosniff'); $response->setHeader('X-XSS-Protection', '1; mode=block'); $response->setHeader('Content-Security-Policy', "default-src 'self';"); return $response; } }
💡

Headers ini simple tapi powerful — block clickjacking, MIME sniffing, XSS.


Kesimpulan

Security bukan feature — ini foundation. SQL injection, XSS, CSRF — semuanya bisa dicegah dengan praktik tepat. Start dari basic: query builder, escape output, validate input, hash password, HTTPS. Terus upgrade dengan headers, rate limiting, file validation. Remember: security adalah process, bukan destination. Stay alert, keep updated, test regularly. Aplikasi lu jauh lebih aman! 🔒