Ketiganya sering disebut bersama, tetapi akar masalah dan mitigasinya berbeda. Prepared statement tidak mencegah XSS; output escaping tidak mencegah CSRF.
SQL Injection: pisahkan data dari perintah
// Aman: nilai dikirim sebagai parameter.
$stmt = $pdo->prepare(
'SELECT id, email FROM users WHERE email = :email'
);
$stmt->execute(['email' => $email]);Nama tabel, kolom, dan arah sorting tidak dapat diparameterkan seperti nilai. Gunakan allowlist ketika bagian query memang dinamis.
$allowedSort = ['name', 'created_at', 'price'];
$sort = in_array($_GET['sort'] ?? '', $allowedSort, true)
? $_GET['sort']
: 'created_at';XSS: escape berdasarkan konteks output
<p><?= htmlspecialchars($comment, ENT_QUOTES, 'UTF-8') ?></p>HTML, atribut, URL, JavaScript, dan CSS memiliki konteks escaping berbeda. Untuk rich text, gunakan sanitizer berbasis allowlist; jangan hanya menghapus tag dengan regex.
CSRF: buktikan request berasal dari aplikasi
// Membuat token
$_SESSION['csrf'] ??= bin2hex(random_bytes(32));
// Memverifikasi
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
http_response_code(419);
exit('Invalid CSRF token');
}Gunakan cookie SameSite, tetapi tetap pasang token pada aksi sensitif. Endpoint GET tidak boleh mengubah state.
Defense in depth
Aplikasi tidak perlu hak DROP atau akses ke semua schema.
Aktifkan Secure, HttpOnly, dan SameSite.
Tambahkan CSP, frame-ancestors, dan nosniff.
Pantau kerentanan package dan runtime.
Catat aksi sensitif tanpa menyimpan rahasia.
Uji payload berbahaya sebagai regression test.
