PHP Security Input Validation Sanitization Password Hashing Csrf Xss SQL Injection
Definisi
Keamanan dasar di PHP bukan cuma soal menutup celah besar. Di project kecil, masalah paling sering justru muncul dari asumsi yang terlalu percaya pada input user, query database yang disusun manual, dan output yang langsung dilempar ke HTML tanpa encoding yang benar.
Topik ini membahas enam area yang hampir selalu muncul di aplikasi web PHP:
- input validation: memastikan data yang masuk sesuai aturan
- sanitization: membersihkan atau menetralkan data supaya aman diproses di konteks tertentu
- password hashing: menyimpan password dengan format yang tidak bisa dibalik
- CSRF: mencegah request palsu yang dikirim atas nama user yang sedang login
- XSS: mencegah script berbahaya masuk ke halaman dan jalan di browser user
- SQL injection: mencegah input user mengubah struktur query SQL
Kalau disederhanakan, alurnya begini: data masuk harus diperiksa, disimpan dengan cara yang benar, lalu ditampilkan kembali dengan encoding yang sesuai konteks.
Gambaran alur ancaman → mitigasi → respons
Diagram ini sengaja memisahkan ancaman, mitigasi, dan respons. Banyak project kecil gagal karena semua masalah dianggap “cukup di-clean dulu”. Padahal tiap risiko punya konteks sendiri.
Input validation
Input validation adalah proses memastikan data yang diterima sesuai bentuk, tipe, panjang, dan aturan bisnis yang diharapkan.
Contoh yang umum:
- email harus format email
- umur harus angka dalam rentang tertentu
- status hanya boleh salah satu dari nilai yang diizinkan
- ID harus numeric atau UUID yang valid
- field wajib tidak boleh kosong
Prinsip dasar
Validasi dilakukan untuk menolak data yang salah, bukan untuk membersihkan data berbahaya.
Itu penting, karena banyak orang mencampur validasi dengan sanitization. Dua hal ini beda:
- validation: apakah datanya boleh diterima?
- sanitization: bagaimana data itu dibersihkan atau dinormalisasi?
Contoh sederhana
$email = $_POST['email'] ?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
http_response_code(422);
exit('Email tidak valid');
}Catatan praktis
- validasi server-side tetap wajib, walaupun sudah ada validasi di frontend
- jangan percaya
requiredHTML sebagai pengaman - jangan hanya cek “tidak kosong”; cek juga format dan batasnya
- validasi paling aman biasanya bersifat allowlist, bukan blocklist
Jebakan umum
- menganggap input dari form admin pasti aman
- hanya validasi di JavaScript lalu server langsung menerima data
- lupa memvalidasi tipe data dari JSON payload
- error message terlalu detail sampai membocorkan pola validasi internal
Sanitization
Sanitization adalah proses menormalkan atau menetralkan data agar aman dipakai di konteks tertentu.
Di PHP, sanitization sering dipakai untuk:
- membersihkan string sebelum disimpan
- menghapus karakter yang tidak diperlukan
- menyiapkan output agar aman untuk HTML, URL, atau atribut
Penting: sanitization bukan pengganti validation
Kalau data memang tidak sesuai aturan, lebih baik ditolak daripada “dibersihkan” lalu diam-diam lolos.
Contoh:
- nama file upload yang berbahaya sebaiknya divalidasi dan dibatasi, bukan cuma diganti karakter anehnya
- HTML user-generated content tidak boleh langsung ditampilkan tanpa strategi yang jelas
Contoh konteks output
echo htmlspecialchars($comment, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');Untuk output ke browser, yang paling penting sering kali bukan “sanitize input”-nya, tapi escape output sesuai konteks.
Jebakan umum
- memakai
strip_tags()lalu menganggap data sudah aman - membersihkan input untuk HTML, lalu memakai data yang sama untuk SQL atau atribut JS tanpa perlakuan lain
- mencampur semua konteks dalam satu fungsi sanitization serba guna
Password hashing
Password tidak boleh disimpan dalam bentuk plaintext atau encryption yang mudah dibalik.
Di PHP, praktik dasar yang benar adalah memakai:
password_hash()untuk membuat hashpassword_verify()untuk membandingkan password login dengan hash yang tersimpan
Contoh
$hash = password_hash($password, PASSWORD_DEFAULT);
if (password_verify($inputPassword, $hash)) {
// login sukses
}Kenapa penting
Kalau database bocor, hash yang kuat jauh lebih aman daripada password mentah. Hash modern juga dirancang lambat, supaya brute force lebih mahal.
Catatan praktis
- jangan bikin hash sendiri dengan
md5(),sha1(), atausha256()untuk password - gunakan
PASSWORD_DEFAULTagar ikut mengikuti rekomendasi PHP terbaru - pastikan proses login selalu memakai
password_verify() - kalau algoritma berubah di versi baru, rencanakan rehash saat login bila perlu
Jebakan umum
- menyimpan password untuk “kemudahan reset”
- membandingkan hash dengan operator string biasa tanpa fungsi verifikasi yang tepat
- memakai hashing umum yang cepat, padahal password butuh algoritma khusus
CSRF
CSRF atau Cross-Site Request Forgery adalah serangan ketika browser user yang sedang login dipakai untuk mengirim request yang tidak ia maksudkan.
Masalah ini muncul karena browser otomatis menyertakan cookie sesi pada request yang cocok. Kalau aplikasi hanya mengandalkan cookie sesi tanpa validasi tambahan, request berbahaya bisa terlihat sah.
Mitigasi dasar
- pakai CSRF token pada form state-changing
- validasi token di server
- gunakan
SameSitecookie bila cocok dengan kebutuhan aplikasi - bedakan request yang mengubah state dengan request yang hanya baca data
Contoh konsep token
session_start();
$_SESSION['csrf_token'] ??= bin2hex(random_bytes(32));Token lalu disertakan sebagai hidden input dan dicek saat submit.
Jebakan umum
- hanya mengandalkan
RefereratauOrigintanpa token - lupa menambahkan proteksi CSRF pada endpoint delete/update
- menganggap API internal tidak butuh CSRF padahal dipanggil lewat browser session
- mematikan CSRF protection demi “lebih cepat selesai”
XSS
XSS atau Cross-Site Scripting terjadi saat attacker berhasil membuat browser user menjalankan script yang tidak diinginkan.
Biasanya sumbernya adalah data user yang ditampilkan kembali ke HTML tanpa escaping yang benar.
Jenis yang sering muncul
- Stored XSS: payload tersimpan di database atau content store
- Reflected XSS: payload muncul kembali langsung dari request
- DOM XSS: manipulasi terjadi di sisi browser lewat JavaScript
Mitigasi dasar
- escape output saat render HTML
- gunakan encoding yang sesuai konteks: HTML text, HTML attribute, URL, atau JavaScript context
- jangan masukkan input user langsung ke
innerHTMLatau HTML mentah - batasi input yang memang boleh mengandung markup
Contoh aman untuk output HTML
echo htmlspecialchars($title, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');Jebakan umum
- mengira data aman hanya karena sudah disimpan di database
- melakukan sanitization input, lalu tetap lupa escape saat output
- menganggap
strip_tags()cukup untuk semua kasus - mencetak data ke atribut HTML tanpa escaping khusus atribut
SQL injection
SQL injection terjadi ketika input user masuk ke query SQL sebagai bagian dari struktur query, bukan sebagai data.
Contoh pola berbahaya:
$sql = "SELECT * FROM users WHERE email = '$email'";Kalau $email tidak dibatasi dengan benar, attacker bisa mengubah makna query.
Mitigasi dasar
- pakai prepared statements
- bind parameter, jangan gabung string untuk input user
- batasi hak akses database sesuai kebutuhan aplikasi
- validasi input untuk memastikan bentuk data masuk akal, walau prepared statement tetap wajib
Contoh dengan PDO
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
$user = $stmt->fetch();Jebakan umum
- mengira escaping manual sudah cukup untuk semua query
- mencampur nilai yang bisa dibind dengan nama tabel atau kolom yang ikut digabung dari input user
- memakai query builder setengah manual lalu menyisipkan fragment mentah
- lupa bahwa validasi bukan pengganti prepared statement
Pola praktis yang aman dipakai ulang
Kalau mau bikin kebiasaan yang stabil di project kecil, urutannya begini:
- Validasi input di boundary request.
- Normalisasi data hanya kalau memang perlu.
- Simpan password dengan
password_hash()dan verifikasi denganpassword_verify(). - Gunakan prepared statements untuk semua query yang membawa input user.
- Escape output setiap kali render ke HTML.
- Pasang CSRF token untuk aksi yang mengubah state.
- Audit konteks output: HTML, atribut, URL, dan JavaScript punya aturan escape yang beda.
Kesalahan umum di project kecil
- terlalu percaya pada form frontend
- menaruh logika keamanan di satu helper “biar ringkas”, lalu dipakai lintas konteks
- menunda prepared statement karena query masih sedikit
- menyimpan password dengan hash cepat atau malah plaintext
- tidak membedakan validasi input, sanitization, dan output encoding
- lupa proteksi CSRF pada endpoint yang terlihat sederhana
- menampilkan data user mentah ke view karena dianggap “cuma admin”
Ringkasnya
Kalau project PHP kecil ingin aman dari masalah dasar, fokus ke empat kebiasaan ini:
- tolak input yang tidak valid
- simpan secret dengan cara yang benar
- escape output sesuai konteks
- jangan pernah menyusun query SQL dengan string mentah dari user
Keamanan dasar bukan soal tools yang rumit. Biasanya yang paling efektif justru kebiasaan kecil yang konsisten.
Referensi terkait
- PHP Manual:
filter_var,htmlspecialchars,password_hash,password_verify - OWASP Cheat Sheet Series
- OWASP Top 10: Injection, XSS, dan CSRF