Remediasi Cross-Site Scripting (XSS) pada aplikasi berbasis PHP dan JavaScript menuntut penerapan prinsip Defense in Depth, yang mencakup validasi input, escaping output, serta konfigurasi header keamanan HTTP. Celah XSS terjadi ketika data yang tidak dipercaya dimasukkan ke dalam halaman web tanpa melalui proses sanitasi atau escaping yang memadai, memungkinkan penyerang mengeksekusi skrip berbahaya di sisi klien (browser korban).
Jenis XSS dan Titik Kerentanan
Sebelum melakukan perbaikan, penting untuk mengenali tiga bentuk utama XSS yang sering ditemukan pada arsitektur PHP-JavaScript:
- Stored XSS (Persistent): Payload disimpan dalam database (misalnya komentar atau profil pengguna) dan dirender ke pengguna lain.
- Reflected XSS (Non-Persistent): Payload dikirim melalui URL atau parameter request (seperti form GET/POST) dan langsung dipantulkan kembali oleh server.
- DOM-based XSS: Kerentanan terjadi sepenuhnya di sisi klien akibat manipulasi DOM menggunakan JavaScript yang tidak aman (misalnya
innerHTML).
Tabel Matriks Remediasi XSS PHP & JavaScript
| Konteks Kerentanan | Metode Tidak Aman | Solusi & Remediasi Aman |
|---|---|---|
| HTML Body Output (PHP) | <?php echo $_GET['name']; ?> |
<?php echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8'); ?> |
| JavaScript Variable (PHP ke JS) | <script>var user = '<?php echo $user; ?>';</script> |
<script>var user = <?php echo json_encode($user, JSON_HEX_TAG); ?>;</script> |
| DOM Manipulation (JavaScript) | element.innerHTML = userInput; |
element.textContent = userInput; atau sanitasi via DOMPurify |
1. Remediasi di Sisi Server (PHP)
Pertahanan utama terhadap Reflected dan Stored XSS harus dilakukan di sisi server sebelum data dirender ke dalam dokumen HTML.
Menggunakan htmlspecialchars()
Setiap kali data dari pengguna (seperti $_GET, $_POST, atau $_COOKIE) dicetak kembali ke halaman HTML, ubah karakter khusus menjadi entitas HTML yang aman menggunakan fungsi htmlspecialchars dengan parameter ENT_QUOTES dan charset UTF-8.
<?php
// Contoh output HTML yang aman
$userInput = "<script>alert('XSS')</script>";
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// Hasil render di browser: <script>alert('XSS')</script>
?>
Menangani Data untuk Konteks JavaScript
Jika Anda perlu memasukkan variabel PHP ke dalam blok skrip JavaScript, penggunaan htmlspecialchars saja tidak cukup karena dapat merusak sintaks atau gagal menangkap semua vektor serangan. Gunakan json_encode() dengan flag pengaman:
<?php
$username = "O'Reilly <script>";
?>
<script>
// json_encode mengonversi string PHP menjadi literal string JavaScript yang aman
let currentUser = <?php echo json_encode($username, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>;
console.log(currentUser);
</script>
2. Remediasi di Sisi Klien (JavaScript)
DOM-based XSS terjadi ketika kode JavaScript mengambil data dari sumber berbahaya (seperti location.search atau location.hash) dan memasukkannya ke dalam sink yang mengeksekusi HTML atau skrip.
Menghindari innerHTML Berbahaya
Jangan pernah menggunakan innerHTML atau outerHTML untuk menyisipkan data mentah dari pengguna. Gunakan properti textContent atau innerText jika Anda hanya ingin menampilkan teks biasa.
// CONTOH TIDAK AMAN
const userInput = location.hash.substring(1);
document.getElementById('output').innerHTML = userInput; // Rentan DOM XSS
// CONTOH AMAN
const safeInput = location.hash.substring(1);
document.getElementById('output').textContent = safeInput; // Browser memperlakukannya sebagai teks murni
Sanitasi HTML dengan Pustaka Pihak Ketiga
Jika aplikasi Anda secara sah memerlukan input berupa HTML terformat (misalnya dari rich text editor), gunakan pustaka sanitasi sisi klien yang teruji seperti DOMPurify sebelum memasukkannya ke dalam DOM.
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.8/purify.min.js"></script>
<script>
let dirtyHTML = "<img src=x onerror=alert(1)>Hello</script>";
let cleanHTML = DOMPurify.sanitize(dirtyHTML);
document.getElementById('content').innerHTML = cleanHTML;
</script>
3. Lapisan Pertahanan Tambahan: Content Security Policy (CSP)
Meskipun kode aplikasi telah diperbaiki, pasang header HTTP Content Security Policy (CSP) untuk memitigasi dampak jika terjadi kelalaian. CSP membatasi sumber dari mana skrip dapat dimuat dan dieksekusi.
<?php
// Mengirim header CSP melalui PHP
header("Content-Security-Policy: default-src 'self'; script-src 'self';");
?>
Dengan konfigurasi di atas, browser akan menolak mengeksekusi skrip inline (seperti atribut onclick atau tag <script>...</script> tanpa nonce) serta skrip yang berasal dari domain eksternal yang tidak dikenal.