XSS (Cross-Site Scripting) terjadi waktu teks dari user ikut dibaca browser sebagai kode, yaitu HTML atau JavaScript. Kode itu lalu jalan di browser orang lain yang membuka halamannya, dengan hak yang sama seperti kode situsmu sendiri. Bayangin MC acara yang membacakan kartu pertanyaan dari penonton. MC yang benar membacakan isi kartu apa adanya: kalau di kartu tertulis "matikan lampu", dia membacakan kalimat itu, bukan berjalan ke saklar. XSS terjadi waktu halamanmu jadi MC yang menuruti isi kartu.
🧪 Tebakan cepat: komentar
<script>alert(1)</script>ditaruh ke halaman dengantextContent. Yang muncul: kotak alert, teks<script>alert(1)</script>apa adanya, atau kotak kosong? Jawabannya ada di bagian "textContent atau innerHTML".
Dari mana namanya
XSS singkatan dari Cross-Site Scripting: script yang ikut masuk ke situs orang lain lewat data. Huruf X dipakai supaya gak tertukar dengan CSS. Polanya selalu sama: data dari user (komentar, nama, kata kunci pencarian, isi URL) dicampur ke HTML tanpa diubah jadi teks biasa.
Contoh: kolom komentar
Server Kelasku menyusun halaman dengan menempel komentar langsung ke HTML:
// server: komentar ditempel mentah ke HTML (rentan)
const komentar = "<script>alert(1)</script>"; // dikirim Budi lewat form
const html = "<p>Komentar: " + komentar + "</p>";
res.send(html);
Browser yang menerima halaman itu gak tahu bagian mana tulisan Budi dan bagian mana buatanmu. Yang dia terima cuma ini:
<p>Komentar: <script>alert(1)</script></p>
- Parser HTML ketemu
<script>, jadi dia membuat elemen script dan langsung menjalankannya. - Hasilnya: muncul alert box di browser setiap orang yang membuka halaman itu.
alert(1)cuma bukti yang gak berbahaya. Script sungguhan di posisi yang sama bisa membaca isi halaman dan mengirim request atas nama user yang sedang login.
Perbaikannya: tampilkan komentar sebagai teks. Caranya dibahas lengkap di materi berikutnya, XSS Prevention. Di JavaScript browser, alat paling dasarnya textContent.
textContent atau innerHTML
Ada dua cara menaruh teks ke elemen. Lab memakai keduanya untuk kotak komentar yang sama:
const box = document.getElementById("komentar");
box.textContent = komentar; // aman: selalu jadi teks
box.innerHTML = komentar; // rentan: dibaca sebagai HTML
textContentmembuat satu text node. Semua karakter, termasuk<dan>, tampil apa adanya.innerHTMLmenyerahkan string ke parser HTML. Tag di dalamnya jadi elemen sungguhan, atribut jadi atribut sungguhan.
Ini yang dibangun browser untuk tiga komentar di Lab:
| Komentar | textContent |
innerHTML |
|---|---|---|
Halo, nama saya Budi |
teks, 20 karakter | teks, 20 karakter |
<script>alert(1)</script> |
teks persis, 25 karakter | elemen <script>, tapi gak dijalankan |
<img src=x onerror=alert(1)> |
teks persis, 28 karakter | elemen <img>: gambar x gagal dimuat, onerror jalan |
Dengan textContent, yang muncul adalah teks <script>alert(1)</script> apa adanya, tanpa alert. Itu jawaban tebakannya.
Baris kedua sering bikin kaget: script yang disisipkan lewat innerHTML memang gak dijalankan browser. Tapi itu bukan berarti aman, karena atribut event seperti onerror tetap jalan. Jadi element.innerHTML = userInput adalah celah XSS. Untuk teks dari user, pakai textContent.
Detektor: alarm, bukan pagar
Latihan materi ini membuat detectXSS(input), fungsi yang menandai input mencurigakan. Dia mengembalikan true kalau input berisi salah satu pola ini, tanpa peduli huruf besar atau kecil:
<script- event handler:
on, beberapa huruf, lalu=, misalnyaonerror=,onload=,onclick= javascript:
function detectXSS(input) {
const lower = input.toLowerCase(); // <SCRIPT> jadi <script>
if (lower.includes("<script")) return true;
if (/on\w+\s*=/.test(lower)) return true; // onerror=, onload=, onclick=
if (lower.includes("javascript:")) return true;
return false;
}
console.log(detectXSS("<script>alert(1)</script>")); // true
console.log(detectXSS("Halo, nama saya Budi")); // false
console.log(detectXSS("<img onerror=alert(1)>")); // true
toLowerCase()membuat<SCRIPT>dan<script>sama-sama ketangkap./on\w+\s*=/artinya:on, satu huruf atau lebih, boleh ada spasi, lalu=.includes("javascript:")menangkap link seperti<a href=javascript:alert(1)>click</a>.
Fungsi seperti ini berguna untuk memberi tanda, misalnya mencatat ke log atau meminta admin meninjau komentar. Tapi dia bukan pertahanan: daftar pola selalu bisa kurang. Pertahanan sesungguhnya ada di cara data ditampilkan, yaitu textContent dan escaping.
⚠️ Jebakan yang sering ketemu
- Mengira
innerHTMLaman karena<script>-nya gak jalan.<img src=x onerror=alert(1)>tetap menjalankanonerror. Teks dari user:textContent. - Lupa huruf besar.
<SCRIPT>alert(1)</SCRIPT>juga tag script. Ubah ke huruf kecil dulu sebelum mencari pola. - Mengandalkan detektor sebagai satu-satunya pagar. Selalu ada cara penulisan yang gak masuk daftar. Detektor cuma alarm.
- Lupa bahwa URL juga input user. Kata kunci di
?q=atau bagian setelah#sama gak bisa dipercayanya dengan isi form.
TL;DR: XSS (Cross-Site Scripting) terjadi waktu teks dari user dibaca browser sebagai kode dan jalan di browser orang lain. Server yang menempel <script>alert(1)</script> mentah ke HTML membuat alert box muncul. element.innerHTML = userInput adalah celah XSS: <script> yang disisipkan memang gak jalan, tapi onerror di <img> jalan. textContent selalu membuat teks biasa. detectXSS mencari <script, on...= dan javascript: tanpa peduli huruf besar, dan berguna sebagai alarm, bukan sebagai pertahanan.