Upaya menyisipkan skrip asing ke halaman web begitu umum sehingga pola ini memiliki nama resmi: Cross-Site Scripting, disingkat XSS — singkatan memakai X agar tidak bertabrakan dengan CSS. Intinya sederhana: skrip yang bukan milik halaman berjalan di dalam konteks halaman tersebut, lengkap dengan identitas origin dan izinnya. Chapter ini memperkenalkan definisi XSS, tiga jenisnya, dampaknya bagi pengunjung, serta mitigasi dari sisi HTML berupa escape karakter khusus dan header Content-Security-Policy. Chapter sebelumnya menyediakan fondasinya; di sini kita beri nama dan bentuk penuhnya.
Definisi dan Tiga Jenis XSS #
Cross-Site Scripting terjadi ketika teks yang seharusnya diperlakukan sebagai data berhasil diurai browser sebagai kode. Skrip asing itulah yang kemudian berjalan dengan kedudukan halaman kita. Berdasarkan jalurnya, XSS dibagi tiga:
- Stored XSS: payload tersimpan di server — misalnya pada kolom komentar — dan terkirim ke setiap pengunjung yang membuka halamannya.
- Reflected XSS: payload menyusup lewat data permintaan, misalnya parameter URL, dan halaman memantulkannya kembali tanpa escape.
- DOM-based XSS: payload diolah sepenuhnya di browser oleh JavaScript; data masuk dari DOM dan keluar ke DOM tanpa menyentuh server.
Dampak pada Pengunjung #
Skrip yang berjalan di konteks halaman mewarisi kedudukan origin halaman itu. Konsekuensinya, skrip tersebut dapat membaca document.cookie — termasuk cookie sesi — mengubah tampilan dan isi halaman, lalu mengirim data yang tampak ke domain lain. Pengunjung tidak melihat perbedaan apa pun: alamat di address bar benar, sementara isinya berjalan di bawah kendali pihak lain. Dari sini mudah dipahami mengapa XSS masuk kategori risiko web yang paling banyak dicatat.
Mitigasi Utama: Escape Karakter Khusus #
Sebagian besar XSS berhenti di satu kebiasaan: setiap data yang dirender di-escape menurut konteksnya. Untuk konteks isi elemen HTML, lima karakter berikut wajib diganti bentuk entitasnya:
| Karakter | Bentuk aman |
|---|---|
& | & |
< | < |
> | > |
" | " |
' | ' |
Perbandingan berikut menunjukkan isian yang sama sebelum dan setelah escape:
<script>ambil(document.cookie)</script><script>ambil(document.cookie)</script>Output yang Dihasilkan:
Pengunjung melihat tulisan "<script>ambil(document.cookie)</script>"
sebagai teks biasa; browser tidak membuat elemen script apa pun.Mari kita telaah perbedaannya:
- Payload asli: bila digabung mentah ke dokumen, browser membuat elemen
scriptyang nyata dan menjalankannya. - Bentuk ter-escape: karakter
<dan>berganti menjadi entitas; dokumen tetap valid, tetapi isinya teks. - Saat dirender: entitas ditampilkan kembali sebagai karakter — sebagai teks, bukan markup yang diurai.
CSP sebagai Lapisan Cadangan #
Content-Security-Policy adalah header HTTP yang menyatakan sumber daya mana saja yang boleh dimuat halaman. Dengan kebijakan berikut, browser hanya mengeksekusi skrip dari situs itu sendiri dan memblokir skrip inline yang menyusup:
Content-Security-Policy: default-src 'self'Escape tetap menjadi pertahanan utama; CSP berperan sebagai jaring pengaman ketika satu jalur render terlepas. Pola berlapis semacam ini kita kenal sebagai defense in depth, sama seperti yang dijelaskan pada chapter Batasan Validasi HTML.
Contoh Halaman: Teks Ter-escape #
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Escape Karakter Khusus dalam Komentar</title>
</head>
<body>
<h1>Escape Karakter Khusus dalam Komentar</h1>
<p>Isian di bawah ini tersimpan setelah proses escape, sehingga skrip di dalamnya tampil sebagai teks biasa dan tidak berjalan:</p>
<pre><code><script>ambil(document.cookie)</script></code></pre>
<p>Perbandingan lima karakter yang wajib di-escape pada konteks isi elemen:</p>
<table>
<thead>
<tr><th>Karakter</th><th>Bentuk aman</th></tr>
</thead>
<tbody>
<tr><td>&</td><td>&amp;</td></tr>
<tr><td><</td><td>&lt;</td></tr>
<tr><td>></td><td>&gt;</td></tr>
<tr><td>"</td><td>&quot;</td></tr>
<tr><td>'</td><td>&#39;</td></tr>
</tbody>
</table>
</body>
</html>Output yang Dihasilkan:
Sebaris teks script tampil apa adanya, diikuti tabel lima karakter
beserta bentuk entitasnya; tidak ada skrip yang berjalan.Mari kita telaah strukturnya:
- Elemen
<pre><code>: menampilkan teks ter-escape dengan format tetap, sehingga tag-nya terbaca jelas sebagai teks. - Tabel lima baris: setiap baris memasangkan karakter dengan bentuk amannya; sel pertama memakai entitas agar karakternya tampil.
- Seluruh halaman: tidak memuat satu skrip pun — contoh keamanan tidak memerlukan JavaScript.
Kini kita mengenal pelindung dan, sama pentingnya, batas-batasnya. Chapter penutup section ini menegaskan pertanyaan yang sering terlewat: sejauh mana HTML murni dapat dipercaya, dan di lapisan mana keamanan yang sesungguhnya berada.
Referensi resmi untuk pendalaman:
- MDN Web Docs — Cross Site Scripting (XSS)
- OWASP — Cross Site Scripting Prevention Cheat Sheet
- MDN Web Docs — Content-Security-Policy
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh-escape.html
Unduh berkas<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Escape Karakter Khusus dalam Komentar</title>
</head>
<body>
<h1>Escape Karakter Khusus dalam Komentar</h1>
<p>Isian di bawah ini tersimpan setelah proses escape, sehingga skrip di dalamnya tampil sebagai teks biasa dan tidak berjalan:</p>
<pre><code><script>ambil(document.cookie)</script></code></pre>
<p>Perbandingan lima karakter yang wajib di-escape pada konteks isi elemen:</p>
<table>
<thead>
<tr><th>Karakter</th><th>Bentuk aman</th></tr>
</thead>
<tbody>
<tr><td>&</td><td>&amp;</td></tr>
<tr><td><</td><td>&lt;</td></tr>
<tr><td>></td><td>&gt;</td></tr>
<tr><td>"</td><td>&quot;</td></tr>
<tr><td>'</td><td>&#39;</td></tr>
</tbody>
</table>
</body>
</html>