Form login, kolom komentar, parameter URL, dan cookie sering terlihat seperti data biasa. Padahal, semua itu berasal dari luar aplikasi dan harus diperlakukan sebagai input yang belum dipercaya. Kesalahan kecil saat memprosesnya dapat membuka jalan bagi cross-site scripting (XSS), SQL injection, atau pembajakan sesi pengguna.
Kabar baiknya, pencegahan tidak selalu membutuhkan sistem yang rumit. Sebagian besar perlindungan dasar bisa dibangun dari kebiasaan coding yang konsisten: validasi input, gunakan query terparameterisasi, lakukan escaping saat output, dan atur sesi dengan benar.
Prinsip pertama: jangan percaya sumber data
Data yang berasal dari pengguna tidak hanya datang dari form HTML. Nilai dari $_GET, $_POST, $_COOKIE, header HTTP, file upload, bahkan data dari API eksternal juga perlu diperlakukan sebagai input tidak tepercaya.
Validasi bukan berarti sekadar memeriksa apakah kolom tidak kosong. Aplikasi perlu menentukan bentuk data yang memang diterima. Misalnya, ID artikel seharusnya berupa bilangan bulat, alamat email perlu memiliki format yang masuk akal, sedangkan nama pengguna mungkin perlu dibatasi panjang dan karakter yang diizinkan.
Namun, validasi saja tidak cukup untuk mencegah XSS. Data yang valid tetap bisa mengandung karakter khusus yang berbahaya ketika ditampilkan kembali ke halaman.
XSS: data berubah menjadi kode di browser
XSS terjadi ketika input pengguna ditampilkan di browser tanpa pengamanan yang sesuai. Contohnya adalah komentar yang berisi potongan HTML atau JavaScript, lalu ditampilkan mentah kepada pengunjung lain.
Untuk konteks HTML, gunakan escaping ketika output, bukan hanya ketika data disimpan. Fungsi htmlspecialchars() dapat mengubah karakter khusus seperti tanda kurung sudut menjadi bentuk aman sehingga browser memperlakukannya sebagai teks.
$nama = $_POST['nama'] ?? '';
echo htmlspecialchars($nama, ENT_QUOTES, 'UTF-8');Perhatikan bahwa escaping harus disesuaikan dengan konteks. Data yang dimasukkan ke HTML, atribut HTML, JavaScript, URL, dan CSS memiliki aturan berbeda. Jangan menganggap satu fungsi pembersih dapat dipakai untuk semua tempat.
Untuk panduan yang lebih rinci tentang konteks output dan pencegahan XSS, lihat XSS Prevention Cheat Sheet dari OWASP.
SQL injection: jangan merakit query dengan konkatenasi
SQL injection biasanya muncul ketika input pengguna langsung digabungkan ke dalam string query. Pola seperti ini terlihat praktis, tetapi membuat input dapat mengubah struktur perintah SQL.
$email = $_POST['email'] ?? '';
$sql = "SELECT * FROM users WHERE email = '$email'";Gunakan prepared statement atau query terparameterisasi. Dengan cara ini, nilai input dipisahkan dari perintah SQL, sehingga database tidak memperlakukannya sebagai bagian dari sintaks query.
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare(
'SELECT id, name FROM users WHERE email = :email'
);
$stmt->execute(['email' => $email]);
$user = $stmt->fetch();Prepared statement bukan alasan untuk mengabaikan validasi. Jika parameter seharusnya berupa angka, tetap pastikan tipenya sesuai. Untuk nama kolom atau urutan ORDER BY yang tidak dapat diikat sebagai parameter, gunakan daftar pilihan yang sudah ditentukan aplikasi, bukan menerima teks mentah dari pengguna.
OWASP merekomendasikan query terparameterisasi sebagai salah satu pertahanan utama terhadap SQL injection.
Sesi login perlu dilindungi seperti password
Setelah login berhasil, browser biasanya menerima session ID melalui cookie. Siapa pun yang memperoleh session ID tersebut berpotensi memakai sesi korban tanpa mengetahui passwordnya. Karena itu, pengamanan sesi sama pentingnya dengan pengamanan proses login.
Beberapa pengaturan dasar yang layak diterapkan:
- Gunakan HTTPS agar cookie tidak mudah disadap dalam perjalanan.
- Aktifkan HttpOnly supaya JavaScript di browser tidak dapat membaca cookie sesi.
- Aktifkan Secure agar cookie hanya dikirim melalui koneksi HTTPS.
- Gunakan SameSite untuk mengurangi risiko pengiriman cookie lintas situs.
- Regenerasi session ID setelah login atau perubahan hak akses untuk mencegah session fixation.
- Terapkan masa kedaluwarsa dan proses logout yang benar.
session_set_cookie_params([
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
session_start();
// Setelah kredensial berhasil diverifikasi:
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];PHP juga menyediakan pengaturan seperti session.use_strict_mode untuk menolak session ID yang tidak dibuat oleh server. Detail pengelolaan sesi dapat dirujuk pada manual keamanan sesi PHP dan panduan Session Management OWASP.
Password tidak boleh dienkripsi lalu dibuka kembali
Password pengguna seharusnya tidak disimpan dalam bentuk teks biasa. Untuk login, aplikasi cukup menyimpan hash password—hasil satu arah yang dirancang agar tidak dapat dikembalikan begitu saja menjadi password asli.
Gunakan fungsi bawaan PHP, bukan membuat algoritma sendiri:
$hash = password_hash($password, PASSWORD_DEFAULT);
if (password_verify($passwordInput, $hash)) {
// Password cocok
}Fungsi password_hash() dirancang untuk membuat hash password menggunakan algoritma yang sesuai dengan dukungan PHP. Hindari MD5 atau SHA-1 untuk password baru. Hash cepat memang berguna untuk integritas file, tetapi justru kurang cocok untuk memperlambat percobaan password.
Pesan error jangan membocorkan isi dapur
Pesan error yang terlalu detail sering membantu pengembang, tetapi juga dapat membantu penyerang. Nama tabel, lokasi file, query SQL, versi library, dan kredensial yang tidak sengaja ikut tercetak sebaiknya tidak muncul di halaman publik.
Di lingkungan produksi, matikan tampilan error ke browser dan arahkan pencatatan ke log yang hanya bisa diakses pihak berwenang. Saat debugging, gunakan lingkungan pengembangan atau log sementara yang kemudian dihapus.
Bedakan pesan untuk pengguna dan catatan internal. Pengguna cukup menerima pesan seperti “Terjadi kesalahan, silakan coba lagi”, sementara detail teknis disimpan di log bersama waktu, endpoint, dan ID permintaan.
Yang bisa dilakukan sekarang
- Cari semua penggunaan langsung
$_GET,$_POST, dan$_COOKIEdalam kode aplikasi. - Ganti query SQL yang dirakit dengan konkatenasi menjadi prepared statement.
- Periksa setiap output yang berasal dari pengguna dan tambahkan escaping sesuai konteksnya.
- Audit konfigurasi cookie sesi: HTTPS,
HttpOnly,Secure, danSameSite. - Pastikan password dibuat dengan
password_hash()dan diverifikasi denganpassword_verify(). - Uji halaman error dalam mode produksi untuk memastikan detail sensitif tidak tampil.
- Tambahkan pengujian keamanan sederhana pada form login, pencarian, komentar, dan endpoint API.
Keamanan aplikasi bukan hasil dari satu plugin atau satu baris konfigurasi. Ia terbentuk dari keputusan kecil yang diulang di setiap endpoint. Ketika input selalu dianggap tidak tepercaya, query dipisahkan dari data, output di-escape, dan sesi dikelola dengan disiplin, banyak celah umum dapat ditutup sebelum menjadi insiden.
Sumber & bacaan lebih lanjut
- OWASP Cross Site Scripting Prevention Cheat Sheet
- OWASP SQL Injection Prevention Cheat Sheet
- OWASP Session Management Cheat Sheet
- PHP Manual: Session Security Management
- PHP Manual: password_hash
– Rio Yotto @rioyotto
