“Validasi” dan “sanitasi” sering diucap bergantian seolah bermakna sama, padahal keduanya adalah strategi berbeda dengan konsekuensi berbeda. Validasi bertanya “apakah data ini sah?” lalu memutuskan menerima atau menolak. Sanitasi bertanya “bagaimana membuat data ini aman atau rapi dipakai?” lalu membersihkannya tanpa memutuskan apa pun. Salah memilih strategi berakibat nyata: email yang salah ketik bisa diam-diam diubah menjadi alamat lain, atau sebaliknya form menolak nama sah hanya karena mengandung tanda kutip. Pada chapter ini kita membedah keduanya, lalu melihat pandangan modern PHP: validasi ketat di pintu masuk, escape sesuai konteks di pintu keluar.

Dua Strategi Menghadapi Input Buruk #

Perbedaan kedua strategi paling mudah dipetakan dalam tabel:

AspekValidasiSanitasi
Pertanyaanapakah data ini sah?bagaimana data ini dirapikan?
Hasilkeputusan: terima atau tolaknilai baru yang sudah dibersihkan
Data aslitidak pernah diubahdiubah menjadi bentuk lain
Cocok untukemail, umur, tanggal, pilihan dari daftarspasi berlebih, kapitalisasi email, teks bebas
Risiko bila salah pakaidata sah ikut tertolakdata berubah makna tanpa sepengetahuan pengguna

Contoh nyatanya: umur "dua puluh" harus divalidasi lalu ditolak — tidak ada “pembersihan” yang masuk akal. Sebaliknya " [email protected] " layak disanitasi ringan (dipangkas spasinya) sebelum divalidasi, karena spasi di ujung jelas bukan bagian dari maksud pengguna. Kuncinya: sanitasi hanya untuk gangguan yang tidak mengubah makna, validasi untuk segala hal yang berhubungan dengan kebenaran.

Validasi Menolak, Bukan Memperbaiki #

Godaan terbesar pemula adalah “memperbaiki” input yang gagal validasi — misalnya mengganti umur 150 dengan 99 supaya lolos. Program yang memperbaiki diam-diam telah mengubah data yang dimaksud pengguna:

php
<?php
function validasiUmur(string $umur): string
{
    $hasil = filter_var($umur, FILTER_VALIDATE_INT, [
        'options' => ['min_range' => 17, 'max_range' => 99],
    ]);

    if ($hasil === false) {
        return "TOLAK: '$umur' bukan umur yang sah";
    }

    return "TERIMA: umur $hasil";
}

echo validasiUmur('25'), "\n";
echo validasiUmur('dua puluh'), "\n";
echo validasiUmur('150'), "\n";

Output yang Dihasilkan:

text
TERIMA: umur 25
TOLAK: 'dua puluh' bukan umur yang sah
TOLAK: '150' bukan umur yang sah

Ketiga kiriman menghasilkan keputusan yang jujur: '25' diterima, 'dua puluh' dan '150' ditolak dengan pesan yang menyebut nilai aslinya. Menolak lalu meminta pengguna mengoreksi sendiri selalu lebih aman daripada menebak maksud pengguna — tebakan yang salah tersimpan diam-diam di database dan baru terasa berbulan-bulan kemudian. Fungsi validasiUmur() sendiri adalah pola dari Chapter Validasi Server-Side : satu fungsi murni yang mengembalikan keputusan.

Normalisasi yang Wajar #

Ada satu bentuk sanitasi yang hampir selalu aman karena tidak mengubah makna: normalisasi bentuk. Dua contoh klasiknya adalah memangkas spasi ujung dan menyeragamkan huruf kecil pada email:

php
<?php
function normalkanEmail(string $email): string
{
    return mb_strtolower(trim($email));
}

$kiriman = '  [email protected]  ';

echo 'Sebelum: [' . $kiriman . "]\n";
echo 'Sesudah: [' . normalkanEmail($kiriman) . "]\n";

Output yang Dihasilkan:

text
Sebelum: [  [email protected]  ]
Sesudah: [[email protected]]

Email pada dasarnya tidak peka kapitalisasi, sehingga [email protected] dan [email protected] dimaknai sistem email sebagai alamat yang sama. Menormalkan ke huruf kecil mencegah satu orang membuat dua akun hanya karena beda gaya mengetik. Bandingkan dengan nama orang: kapitalisasi adalah bagian dari identitas (Budi, BUDI, Budi bisa jadi orang yang berbeda dalam budaya tertentu), jadi nama tidak boleh dipaksa ke satu bentuk. Aturan praktisnya: normalkan hanya aspek yang sistem target (email, DNS) memang tidak membedakannya.

Filter Sanitasi Bawaan PHP dan Statusnya #

Extension filter menyediakan keluarga FILTER_SANITIZE_* yang membersihkan nilai secara agresif — dan salah satunya sudah pensiun. Sejak PHP 8.1, konstanta FILTER_SANITIZE_STRING (dan FILTER_SANITIZE_STRIPPED) berstatus deprecated: keduanya masih ada dan memicu Deprecated saat dipakai, namun tidak boleh lagi dipakai di kode baru:

php
<?php
// Gaya lama — HINDARI: constant berikut deprecated sejak PHP 8.1
$namaBersih = filter_var($nama, FILTER_SANITIZE_STRING);

Filter lain seperti FILTER_SANITIZE_EMAIL dan FILTER_SANITIZE_URL masih hidup, tetapi perilakunya layak dicermati sebelum dipakai:

php
<?php
$mentah = 'budi(percobaan)@example.com';

$bersih = filter_var($mentah, FILTER_SANITIZE_EMAIL);

var_dump($mentah);
var_dump($bersih);
var_dump(filter_var($mentah, FILTER_VALIDATE_EMAIL));
var_dump(filter_var($bersih, FILTER_VALIDATE_EMAIL));

Output yang Dihasilkan:

text
string(27) "budi(percobaan)@example.com"
string(25) "[email protected]"
bool(false)
string(25) "[email protected]"

Perhatikan hasilnya dengan saksama. Email mentah budi(percobaan)@example.com gagal validasi. Setelah melewati FILTER_SANITIZE_EMAIL, tanda kurung dibuang dan hasilnya menjadi [email protected] — yang kini lolos validasi. Inilah bahaya tersembunyi sanitasi: filter mengubah identitas data menjadi alamat lain yang mungkin tidak dimaksudkan siapa pun. Email “yang berhasil dibersihkan” bukan lagi email yang diketik pengguna, dan surat penting bisa terkirim ke alamat hasil pembersihan. Untuk data bermakna identitas seperti email, keputusan yang benar adalah menolak mentah-mentah dan meminta pengguna mengoreksi.

Escape di Titik Keluaran #

Jika sanitasi menyimpan bukan jawabannya, bagaimana data yang mengandung karakter “berbahaya” — <b>, tanda kutip, & — ditampilkan dengan aman? Jawaban modern PHP: data disimpan apa adanya, dan di-escape pada saat ditampilkan, sesuai konteks keluarannya:

php
<?php
$nama = "<b>Budi</b>";

// Data mentah disimpan apa adanya
echo "Data yang disimpan: $nama\n";

// Di-escape hanya saat ditampilkan sebagai HTML
echo 'Tampilan di HTML: ' . htmlspecialchars($nama, ENT_QUOTES, 'UTF-8') . "\n";

Output yang Dihasilkan:

text
Data yang disimpan: <b>Budi</b>
Tampilan di HTML: &lt;b&gt;Budi&lt;/b&gt;

Baris pertama membuktikan data asli tetap utuh — <b>Budi</b> tersimpan apa adanya. Baris kedua mengubah hanya representasinya untuk konteks HTML: htmlspecialchars() mengganti <, >, &, dan tanda kutip menjadi entitas seperti &lt; sehingga browser menampilkannya sebagai teks, bukan mengeksekusinya sebagai tag. Escape terjadi di lapisan tampilan, sekali setiap kali data keluar, dan tidak pernah mengotori data yang tersimpan. Sejak PHP 8.1, htmlspecialchars() bahkan memakai ENT_QUOTES | ENT_SUBSTITUTE sebagai bawaan sehingga tanda kutip tunggal ikut di-escape tanpa perlu kita atur.

Konteks keluaranAlat escape yang tepat
Isi dan atribut HTMLhtmlspecialchars()
Query SQLprepared statement (bukan escape manual)
Query string URLhttp_build_query() atau urlencode()
Body JSONjson_encode()

Menyimpan data yang sudah ter-escape HTML justru menciptakan masalah baru: data tersebut tampil ganda escape (&amp;lt;) saat di-escape lagi di tampilan, dan tidak bisa dipakai untuk konteks lain. Panduan lengkap dari PHP: simpan mentah, escape di pintu keluar sesuai konteks — prinsip yang juga menjadi fondasi Section Web Security . Untuk konteks SQL, prepared statement adalah satu-satunya jawaban yang benar; mekanismenya dibahas tuntas pada Section Database dan SQL .

Ringkasan #

Validasi dan sanitasi menjawab pertanyaan berbeda: validasi memutuskan menerima atau menolak tanpa mengubah data, sanitasi membersihkan data dengan risiko mengubah maknanya. Praktik yang sehat di PHP modern: normalkan aspek yang memang tidak dibedakan sistem target (spasi ujung, kapitalisasi email), validasi ketat selebihnya, simpan data apa adanya, lalu escape sesuai konteks di setiap titik keluaran — htmlspecialchars() untuk HTML, prepared statement untuk SQL. Keluarga FILTER_SANITIZE_* tinggal sejarah yang perlu dipahami, bukan alat utama. Dengan kerangka ini, kita siap membedah dua validator spesifik yang paling sering dipakai: email dan URL pada chapter berikutnya.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

contoh.php

Unduh berkas
php
<?php
// contoh.php — alur lengkap: normalisasi → validasi → simpan → escape saat keluaran.

/**
 * Memvalidasi format email terhadap nilai yang sudah dirapikan.
 */
function validasiEmail(string $email): bool
{
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

$kiriman = '  [email protected] ';

// Tahap 1: normalisasi bentuk (spasi ujung, kapitalisasi)
$bersih = trim($kiriman);

if (!validasiEmail($bersih)) {
    echo "TOLAK: '$kiriman' bukan email yang sah\n";
    exit;
}

// Tahap 2: simpan bentuk ternormalisasi
$tersimpan = mb_strtolower($bersih);
echo "Tersimpan di database: $tersimpan\n";

// Tahap 3: escape hanya saat data keluar ke konteks HTML
echo 'Ditampilkan di HTML: ' . htmlspecialchars($tersimpan, ENT_QUOTES, 'UTF-8') . "\n";