Beranda

Security

XSS (Cross-Site Scripting): Cara Kerja S...

XSS (Cross-Site Scripting): Cara Kerja Serangan dan Pencegah...

XSS (Cross-Site Scripting): Cara Kerja Serangan dan Pencegahannya

Bedah tuntas cara kerja Cross-Site Scripting (XSS): tiga jenisnya, pola berpikir penyerang saat mencari celah, dan cara menambalnya dengan output encoding, CSP, HttpOnly, serta sanitasi sisi klien. Artikel pertama seri keamanan web Yowisben.

XSS (Cross-Site Scripting): Cara Kerja Serangan dan Pencegahannya

Cross-Site Scripting, atau XSS, adalah salah satu celah keamanan web yang paling tua sekaligus paling sering muncul kembali. Inti masalahnya sederhana: aplikasi menampilkan data dari pengguna tanpa membersihkannya lebih dulu, sehingga peramban korban ikut menjalankan kode yang bukan milik Anda. Dari situ penyerang bisa mencuri sesi login, mengubah tampilan halaman, atau menyeret pengguna melakukan tindakan yang tidak mereka sadari.

Tutorial ini membedah cara kerja XSS langkah demi langkah — bukan supaya Anda menyerang siapa pun, melainkan supaya Anda paham betul di titik mana kode Anda bisa ditembus, lalu menutupnya dengan benar. Ini adalah artikel pertama dari seri keamanan web Yowisben; artikel berikutnya akan membahas kerentanan lain seperti SQL Injection, CSRF, dan SSRF dengan pendekatan yang sama.

Peringatan etika. Semua teknik di sini hanya boleh diuji pada aplikasi milik Anda sendiri atau lab yang memang disediakan untuk latihan. Mencoba celah pada sistem orang lain tanpa izin tertulis adalah tindak pidana di banyak yurisdiksi, termasuk Indonesia lewat UU ITE.

Prasyarat

  • Pemahaman dasar HTML dan JavaScript.
  • Familiar dengan konsep request/response HTTP.
  • Opsional: lingkungan lab seperti OWASP Juice Shop atau DVWA untuk mencoba sendiri secara legal.

1. Pahami akar masalahnya: data vs kode

Peramban tidak bisa membedakan mana teks yang "hanya untuk ditampilkan" dan mana yang "harus dijalankan". Keduanya sama-sama sampai sebagai karakter di dalam dokumen HTML. Batas antara data dan kode ditentukan sepenuhnya oleh konteks tempat data itu ditaruh.

Perhatikan potongan kode server yang rentan berikut. Anggap sebuah fitur "salam" yang menyapa pengguna berdasarkan parameter URL:

<?php
// RENTAN — jangan tiru
$nama = $_GET['nama'];
echo "<h1>Halo, " . $nama . "!</h1>";

Kalau pengunjung membuka ?nama=Budi, keluarannya wajar:

<h1>Halo, Budi!</h1>

Tapi karena $nama disisipkan mentah ke dalam HTML, pengunjung bisa mengganti nilainya dengan markup, bukan sekadar teks:

?nama=<script>alert(document.domain)</script>

Hasilnya, peramban menerima dan mengeksekusi:

<h1>Halo, <script>alert(document.domain)</script>!</h1>

alert() di sini hanya bukti konsep yang tidak berbahaya — sekadar menandakan bahwa JavaScript sembarang bisa berjalan. Begitu titik ini terbuka, apa pun yang bisa dilakukan JavaScript di halaman itu juga terbuka.

2. Kenali tiga jenis XSS

XSS bukan satu hal tunggal. Membedakan jenisnya penting karena cara pertahanannya sedikit berbeda.

Reflected XSS

Payload ikut di dalam request (biasanya di URL atau form) dan langsung "dipantulkan" kembali di response tanpa disimpan. Contoh fitur salam di atas termasuk kategori ini. Penyerang perlu membujuk korban mengeklik tautan yang sudah disisipi payload — lewat email, chat, atau iklan.

Stored XSS

Payload disimpan di server — di kolom komentar, profil, nama produk, apa pun — lalu tampil ke setiap pengunjung yang membuka halaman tersebut. Ini yang paling berbahaya karena tidak butuh tautan khusus; korban cukup membuka halaman normal. Satu komentar jahat bisa mengenai ribuan orang.

DOM-based XSS

Celah ada murni di sisi klien. JavaScript di halaman mengambil data dari sumber seperti location.hash lalu menaruhnya ke DOM lewat properti berbahaya seperti innerHTML, tanpa server pernah ikut campur.

// RENTAN — sink innerHTML dari sumber yang dikontrol pengguna
const q = decodeURIComponent(location.hash.slice(1));
document.getElementById('hasil').innerHTML = 'Mencari: ' + q;

Karena payload tidak pernah melewati server, filter di sisi server tidak akan melihatnya sama sekali.

3. Ikuti pola kerja penyerang

Memahami urutan berpikir penyerang membantu Anda menutup celah di tahap yang tepat. Polanya konsisten:

  1. Pemetaan titik masuk. Penyerang mendaftar semua tempat input pengguna bisa masuk: parameter URL, field form, header, komentar, nama file yang diunggah, bahkan data dari API pihak ketiga.
  2. Uji pemantulan. Ia memasukkan penanda unik yang tidak berbahaya, misalnya xyz123, lalu memeriksa apakah string itu muncul di HTML response dan di konteks apa — di dalam teks, di atribut, atau di dalam tag <script>.
  3. Uji karakter khusus. Berikutnya ia menguji apakah karakter seperti <, >, ", dan ' lolos tanpa diubah. Kalau < tetap < dan bukan &lt;, konteksnya kemungkinan bisa ditembus.
  4. Sesuaikan payload dengan konteks. Payload untuk teks biasa berbeda dari payload di dalam atribut atau di dalam JavaScript. Skrip otomatis seperti pemindai kerentanan mencoba banyak varian sekaligus.
  5. Jadikan senjata. Setelah eksekusi terbukti, alert() diganti dengan aksi nyata: mencuri token sesi, menekan tombol atas nama korban, atau mengarahkan ke halaman palsu.

Tujuan akhir yang umum adalah mengambil alih sesi. Secara konsep, script berjalan di peramban korban dengan hak akses yang sama seperti korban — itulah mengapa dampaknya bisa separah pengambilalihan akun penuh.

4. Perbaikan utama: encoding sesuai konteks

Pertahanan paling fundamental bukan "memblokir kata script", melainkan melakukan output encoding sesuai konteks tempat data ditampilkan. Prinsipnya: perlakukan semua data dari pengguna sebagai teks, bukan markup.

Untuk konteks HTML biasa, ubah karakter khusus menjadi entitasnya:

Karakter Menjadi
< &lt;
> &gt;
" &quot;
' &#x27;
& &amp;

Versi aman dari fitur salam tadi:

<?php
$nama = $_GET['nama'];
// Encode ke konteks HTML sebelum ditampilkan
echo "<h1>Halo, " . htmlspecialchars($nama, ENT_QUOTES, 'UTF-8') . "!</h1>";

Sekarang payload <script> hanya tampil sebagai teks harfiah &lt;script&gt; dan tidak dieksekusi.

Tips. Jangan menulis fungsi encoding sendiri. Gunakan yang sudah teruji: htmlspecialchars di PHP, dan di framework modern gunakan mekanisme auto-escaping bawaan template engine (Blade, Twig, Jinja2, ERB) yang meng-encode secara default.

Poin penting: konteks menentukan encoder. Data yang ditaruh di dalam atribut, di dalam URL, atau di dalam blok JavaScript butuh encoding yang berbeda. Menaruh data yang dikontrol pengguna langsung ke dalam <script> sebaiknya dihindari sama sekali.

5. Amankan sisi klien untuk DOM-based XSS

Encoding di server tidak menolong DOM-based XSS. Di sisi klien, aturannya adalah menghindari "sink" berbahaya dan memilih API yang aman.

// RENTAN
element.innerHTML = dataPengguna;

// AMAN — textContent memperlakukan isinya murni sebagai teks
element.textContent = dataPengguna;

Gunakan textContent alih-alih innerHTML bila Anda hanya perlu menampilkan teks. Jika memang harus merender HTML kaya dari pengguna (misalnya editor komentar), sanitasi dengan pustaka khusus seperti DOMPurify, jangan menyaring manual.

import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(htmlDariPengguna);

6. Pasang lapisan pertahanan tambahan

Encoding adalah garis pertama, tapi pertahanan berlapis membuat satu kesalahan tidak langsung berujung bencana.

Content Security Policy (CSP). Header ini memberi tahu peramban sumber skrip mana yang boleh dijalankan. Bila dikonfigurasi ketat, script inline dari penyerang akan diblokir meskipun berhasil disisipkan.

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'

Atribut cookie HttpOnly. Cookie sesi yang ditandai HttpOnly tidak bisa dibaca oleh JavaScript, sehingga upaya membaca token sesi lewat script menjadi gagal.

Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict

Validasi input di sisi masuk. Terapkan allowlist: kalau sebuah field seharusnya berisi angka, tolak apa pun yang bukan angka. Validasi input bukan pengganti output encoding, tapi mempersempit permukaan serang.

Catatan. Urutan prioritasnya: output encoding sesuai konteks adalah wajib, CSP dan HttpOnly adalah jaring pengaman, dan validasi input adalah penyaring awal. Ketiganya saling melengkapi, bukan saling menggantikan.

7. Uji aplikasi Anda sendiri

Setelah menambal, verifikasi. Cara paling sederhana adalah memasukkan penanda uji ke setiap field dan memeriksa bagaimana ia muncul di HTML response — apakah karakter < sudah menjadi &lt;.

Untuk cakupan lebih luas, gunakan alat yang memang dirancang untuk itu di lingkungan Anda sendiri:

  • OWASP ZAP — pemindai keamanan gratis yang bisa mengaudit banyak titik sekaligus.
  • Linter dan analisis statis — banyak yang menandai penggunaan innerHTML atau output tanpa escaping.
  • Code review — periksa setiap tempat data pengguna keluar ke HTML, dan pastikan melewati encoder.

Tips. Masukkan pengecekan XSS ke dalam pipeline CI/CD. Menemukan celah saat commit jauh lebih murah daripada setelah rilis.

Penutup

XSS bertahan lama bukan karena rumit, melainkan karena mudah terlewat: satu baris output tanpa encoding sudah cukup. Kabar baiknya, pertahanannya juga jelas dan dapat diandalkan — encode output sesuai konteks, hindari sink berbahaya di sisi klien, dan pasang CSP serta HttpOnly sebagai jaring pengaman.

Kalau Anda hanya mengambil satu hal dari tutorial ini, ambil ini: jangan pernah menaruh data dari pengguna langsung ke HTML tanpa meng-encode-nya lebih dulu.

Langkah berikutnya

  • Audit satu aplikasi Anda: daftar semua tempat input pengguna keluar ke halaman, pastikan tiap titik ter-encode.
  • Coba OWASP Juice Shop untuk berlatih menemukan dan menambal XSS secara legal.
  • Nantikan artikel berikutnya di seri ini: SQL Injection — bagaimana query yang tidak diparametrisasi membuka jalan ke seluruh basis data, dan cara menutupnya.

Post Terkait

CSRF untuk Pemula: Ketika Browser Anda Sendiri Jadi Senjata Penyerang

CSRF memanfaatkan satu kebiasaan browser: mengirim cookie sesi ke domain tujuan tanpa peduli halaman mana yang memicunya...

19 Agt 2026

CORS untuk Pemula: Bukan Serangan, Tapi Salah Konfigurasinya Bisa Fatal

Berbeda dengan XSS yang jelas-jelas sebuah serangan, CORS sebenarnya adalah mekanisme keamanan browser. Masalahnya muncu...

19 Agt 2026

SQL Injection untuk Pemula: Saat Satu Tanda Kutip Bisa Membobol Database

Kerentanan berumur lebih dari dua dekade ini masih rutin muncul di berita kebocoran data. Pahami kenapa query yang diben...

19 Agt 2026

© 2026 Yowisben. Semua hak dilindungi.

Powered by LONTAR CMS v1.60.1