Form pendaftaran, checkout, komentar, dan pengaturan profil adalah pintu masuk data ke dalam aplikasi web. Masalahnya, data yang terlihat benar di browser belum tentu aman atau sesuai aturan ketika sampai ke server.
Inilah alasan validasi tidak boleh hanya diletakkan di JavaScript. Browser membantu pengguna mengisi form dengan nyaman, tetapi server dan database tetap harus menjadi lapisan pemeriksaan terakhir. Jika salah satu lapisan dilewati, data yang rusak bisa menyebar ke laporan, notifikasi, proses pembayaran, bahkan sistem lain yang memakai data tersebut.
Validasi di browser bukan penjaga utama
Validasi sisi klien atau client-side validation berjalan di browser pengguna. Contohnya, JavaScript memeriksa apakah alamat email memiliki format yang benar atau apakah kolom nama tidak boleh kosong.
Lapisan ini berguna karena memberikan umpan balik cepat. Pengguna tidak perlu mengirim form ke server hanya untuk mengetahui bahwa nomor telepon belum diisi. Namun, semua aturan di browser dapat dilewati. Pengguna bisa mematikan JavaScript, mengubah request melalui DevTools, atau mengirim data langsung menggunakan curl dan Postman.
Jadi, validasi di browser sebaiknya dianggap sebagai bantuan pengalaman pengguna, bukan sebagai mekanisme keamanan.
const email = document.querySelector('#email').value.trim();
if (!email.includes('@')) {
showError('Masukkan alamat email yang valid');
}Kode seperti ini membuat form terasa lebih ramah, tetapi tidak menjamin bahwa server hanya menerima alamat email yang valid.
Server harus memeriksa ulang semua aturan penting
Server menerima request dari berbagai sumber, bukan hanya dari form yang dibuat oleh tim Anda. Karena itu, pemeriksaan di server harus berdiri sendiri dan tidak menganggap browser sudah melakukan tugasnya.
Misalnya, sebuah endpoint menerima data pendaftaran pengguna. Server setidaknya perlu memeriksa:
- Kolom wajib benar-benar tersedia.
- Jenis data sesuai harapan.
- Panjang teks masih dalam batas yang wajar.
- Nilai berada dalam daftar pilihan yang diperbolehkan.
- Email belum digunakan jika harus unik.
- Angka tidak bernilai negatif jika konteksnya tidak memungkinkan.
Contoh sederhana dalam PHP:
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT);
$errors = [];
if ($name === '') {
$errors['name'] = 'Nama wajib diisi';
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Email tidak valid';
}
if ($age === false || $age < 18) {
$errors['age'] = 'Usia minimal 18 tahun';
}Contoh tersebut bukan perlindungan lengkap untuk semua aplikasi, tetapi menunjukkan prinsip penting: server membaca, membersihkan, dan memeriksa data sebelum memprosesnya.
Bedakan data yang “valid” dan data yang “masuk akal”
Data bisa lolos pemeriksaan format, tetapi tetap tidak masuk akal secara bisnis. Nilai quantity=3 mungkin valid sebagai angka, tetapi belum tentu boleh dibeli jika stok hanya tersisa satu.
Karena itu, validasi biasanya memiliki dua lapisan:
- Validasi bentuk: memeriksa tipe, format, panjang, dan struktur data.
- Validasi aturan bisnis: memeriksa apakah data tersebut diperbolehkan dalam kondisi aplikasi saat ini.
Contohnya pada sistem pemesanan, server perlu memeriksa harga dan stok dari sumber yang dipercaya. Jangan menerima harga dari browser hanya karena nilainya terlihat benar. Harga yang dikirim pengguna dapat diubah sebelum request dikirim.
Server sebaiknya menerima ID produk dan jumlah barang, lalu mengambil harga terbaru dari database. Dengan begitu, nilai penting tidak ditentukan oleh input yang berada di luar kendali aplikasi.
Database juga perlu diberi pagar
Validasi aplikasi sangat penting, tetapi database sebaiknya tidak dibiarkan tanpa perlindungan. Constraint database dapat mencegah data tertentu masuk meskipun ada bug di kode aplikasi.
Beberapa pagar yang umum digunakan antara lain:
NOT NULLuntuk kolom yang wajib memiliki nilai.UNIQUEuntuk email, username, atau kode yang tidak boleh sama.CHECKuntuk membatasi nilai tertentu, jika didukung oleh sistem database.- Foreign key untuk menjaga hubungan antar tabel tetap konsisten.
Misalnya, email pengguna harus unik. Pemeriksaan di PHP tetap diperlukan agar pesan error dapat dibuat dengan baik, tetapi constraint UNIQUE di database menjadi perlindungan terakhir ketika dua request datang hampir bersamaan.
Gunakan prepared statement, bukan menyusun SQL dari input
Setelah data divalidasi, cara memasukkannya ke database juga menentukan keamanan aplikasi. Hindari menyusun query SQL dengan penggabungan string dari input pengguna.
Gunakan prepared statement agar nilai input diperlakukan sebagai data, bukan sebagai bagian dari perintah SQL.
$stmt = $pdo->prepare(
'INSERT INTO users (name, email) VALUES (:name, :email)'
);
$stmt->execute([
':name' => $name,
':email' => $email
]);Prepared statement tidak menggantikan validasi. Keduanya memiliki tujuan berbeda: validasi memastikan data sesuai aturan, sedangkan prepared statement membantu memisahkan data dari struktur query.
Jangan lupa normalisasi dan output encoding
Data yang disimpan juga perlu dipikirkan bentuknya. Email biasanya dapat dinormalisasi dengan menghapus spasi di awal dan akhir. Namun, jangan sembarangan mengubah semua input karena beberapa data memang peka terhadap huruf besar-kecil atau format tertentu.
Selain itu, data yang aman disimpan belum tentu aman ditampilkan. Nama atau komentar pengguna dapat mengandung karakter khusus yang berbahaya jika langsung dimasukkan ke HTML. Saat menampilkan data, gunakan output escaping sesuai konteksnya, misalnya HTML, atribut, JavaScript, atau URL.
Prinsip sederhananya: bersihkan sesuai kebutuhan, validasi sesuai aturan, dan lakukan escaping ketika data keluar menuju tampilan.
Bagaimana membuat alurnya lebih rapi?
Dalam proyek kecil, validasi sering ditulis langsung di dalam controller. Cara ini memang cepat, tetapi akan merepotkan ketika aturan yang sama dipakai oleh form web, API, dan proses impor data.
Pertimbangkan untuk memisahkan beberapa bagian berikut:
- Request parsing: mengambil data dari request.
- Validation: memeriksa format dan aturan dasar.
- Business rule: memeriksa kondisi aplikasi dan database.
- Persistence: menyimpan data menggunakan query yang aman.
- Response: mengembalikan pesan yang dapat dipahami pengguna.
Pemisahan ini membuat kode lebih mudah diuji. Anda dapat menguji aturan validasi tanpa harus selalu mengirim request melalui browser.
Yang bisa dilakukan sekarang
Audit satu form penting di aplikasi Anda, misalnya form pendaftaran atau checkout. Pastikan aturan berikut tersedia di server:
- Input kosong dan input dengan tipe salah ditolak.
- Panjang input memiliki batas.
- Nilai penting tidak dipercaya dari browser.
- Query database menggunakan prepared statement.
- Kolom penting memiliki constraint yang sesuai.
- Pesan error tidak membocorkan detail internal database.
- Data di-escape ketika ditampilkan kembali.
Validasi yang baik bukan berarti membuat pengguna melewati banyak hambatan. Tujuannya adalah memastikan data tetap benar tanpa mengorbankan pengalaman penggunaan. Browser dapat membantu pengguna, server dapat menegakkan aturan, dan database dapat menjadi pagar terakhir. Ketiganya bekerja bersama agar kesalahan kecil tidak berubah menjadi masalah besar di seluruh aplikasi.
– Rio Yotto @rioyotto
