Menu

htmlspecialchars() PHP: Escape HTML dan Cegah XSS

htmlspecialchars($text) mengubah lima karakter yang punya arti di HTML (& < > " ') menjadi entity, sehingga input pengguna ditampilkan sebagai teks alih-alih dijalankan sebagai markup. Pelajari ENT_QUOTES, escape atribut, double encoding, htmlentities, dan strip_tags.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

htmlspecialchars($text) mengubah &, <, >, ", dan ' menjadi &amp;, &lt;, &gt;, &quot;, dan &#039;. Panggil fungsi ini pada setiap input pengguna yang kamu cetak ke halaman, maka input itu ditampilkan sebagai teks alih-alih dibaca sebagai HTML.

Blok ini mencetak komentar yang sama dua kali, sekali mentah dan sekali sudah di-escape. Jalankan, bandingkan kedua barisnya di tab Halaman, lalu ketik HTML-mu sendiri ke form, misalnya <h1>big</h1> atau <img src=x>, dan tekan Show.

Di baris mentah, browser menuruti tag-nya: kata yang tebal jadi tebal, dan pengunjung yang mengetik <script> membuat script-nya dijalankan di browser setiap pembaca lain. Serangan ini disebut cross-site scripting (XSS). Di baris yang sudah di-escape, karakter yang sama datang sebagai &lt;b&gt; dan browser menggambarnya sebagai teks. Pindah ke tab Output untuk melihat entity yang benar-benar dicetak PHP.

Apa saja yang diubah htmlspecialchars

Lima karakter, tidak lebih. Huruf, aksen, dan emoji lewat tanpa diubah.

& ada di daftar itu karena karakter inilah yang mengawali setiap entity: jika dibiarkan, komentar yang menyebut &lt; akan ditampilkan sebagai <.

Escape atribut, bukan hanya teks

Input pengguna di dalam atribut juga perlu di-escape. Tanpa escape, tanda kutip di dalam nilai menutup atributnya dan sisa input berubah menjadi atribut baru. Di sini "name" menyelipkan atribut style; jalankan dan lihat kedua kotaknya.

Di kotak yang tidak aman, browser melihat value="Ada" diikuti atribut style baru, jadi kotaknya berubah merah dan hanya menampilkan Ada. Penyerang akan menulis onfocus="..." di situ alih-alih style, dan kodenya akan berjalan. Di kotak yang aman, setiap " menjadi &quot;, jadi seluruh string tetap berada di dalam value dan ditampilkan seperti yang diketik.

Sejak PHP 8.1, flag default-nya adalah ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, jadi tanda kutip tunggal juga di-escape dan atribut yang ditulis dengan '...' aman. Kode lama sering memberikan ENT_QUOTES secara manual, dan di PHP 7 ke bawah hal itu memang wajib:

Helper singkat untuk template

Menulis htmlspecialchars($x, ENT_QUOTES, 'UTF-8') puluhan kali di template terasa berisik, jadi sebagian besar proyek membungkusnya dalam fungsi berhuruf satu. Template engine seperti Twig dan Blade melakukan hal yang sama secara otomatis untuk setiap {{ $var }}.

Tipe ?string dan ?? '' itu penting: memberikan null ke htmlspecialchars() sudah deprecated sejak PHP 8.1, dan PHP-mu sendiri akan mencetak deprecation notice untuk setiap pengguna yang tidak punya bio.

Double encoding dan htmlspecialchars_decode

Jika sebuah nilai di-escape dua kali, pembaca melihat entity-nya: & menjadi &amp; pada escape pertama dan &amp;amp; pada escape kedua, yang ditampilkan browser sebagai &amp;. Biasanya ini berarti nilainya di-escape saat disimpan dan sekali lagi saat dicetak. Berikan double_encode: false agar entity yang sudah ada tidak diubah, dan pakai htmlspecialchars_decode() untuk mengembalikannya.

Solusi sebenarnya adalah menyimpan teks mentah dan hanya melakukan escape saat output. double_encode: false ditujukan untuk teks yang sudah berisi entity yang tidak kamu buat, seperti feed yang diimpor.

htmlspecialchars vs htmlentities vs strip_tags

Ketiganya sering tertukar. Blok ini menjalankan semuanya pada input yang sama dan menunjukkan, untuk masing-masing, apa yang dicetak PHP dan bagaimana browser menampilkannya:

  • htmlspecialchars() melakukan escape pada lima karakter HTML. Pakai untuk teks apa pun yang kamu cetak ke HTML.
  • htmlentities() juga mengubah é menjadi &eacute;. Fungsi ini berguna saat halaman belum memakai UTF-8; sekarang ia hanya membuat source lebih sulit dibaca.
  • strip_tags() menghapus tag dan mempertahankan teksnya. Fungsi ini untuk mengubah HTML menjadi teks biasa (pratinjau email, meta description), bukan untuk keamanan: baris terakhir menunjukkan bahwa <b> yang diizinkan tetap mempertahankan onclick-nya, dan teks yang ditaruh di dalam atribut sama sekali tidak disentuh.

Saat htmlspecialchars tidak cukup

htmlspecialchars() adalah escape yang tepat untuk teks HTML dan atribut yang diberi tanda kutip. Bagian lain di halaman punya aturan lain:

  • Di dalam URL, http_build_query() atau urlencode() meng-encode nilainya; htmlspecialchars() lalu membuat & di antara parameter menjadi HTML yang valid.
  • Di JavaScript, json_encode() menghasilkan nilai JS yang valid, dan JSON_HEX_TAG mengubah < dan > menjadi \u003C dan \u003E, sehingga </script> di dalam data tidak bisa menutup tag script.
  • Jangan pernah mencetak input pengguna ke href tanpa memeriksa skemanya: htmlspecialchars('javascript:alert(1)') tidak berubah dan tetap berjalan saat diklik. Terima hanya URL http dan https, seperti yang ditunjukkan di halaman filter_var.

Untuk pemrosesan form yang menggabungkan semua ini, lihat form PHP.

Pertanyaan yang sering diajukan

Apa fungsi htmlspecialchars di PHP?

Fungsi ini mengganti &, <, >, ", dan ' dengan &amp;, &lt;, &gt;, &quot;, dan &#039;. Browser lalu menampilkan karakter-karakter itu alih-alih membacanya sebagai HTML, jadi <script> yang diketik ke form ditampilkan sebagai teks dan tidak pernah dijalankan.

Apa perbedaan htmlspecialchars dan htmlentities?

htmlspecialchars() hanya mengubah lima karakter yang istimewa di HTML. htmlentities() juga mengubah setiap karakter yang punya named entity, jadi café menjadi caf&eacute;. Pada halaman UTF-8 keduanya sama amannya, dan htmlspecialchars() membuat output tetap mudah dibaca, jadi fungsi itulah yang biasa dipilih.

Apakah saya masih perlu ENT_QUOTES di PHP 8?

Tidak untuk keamanan: sejak PHP 8.1, flag default-nya adalah ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, jadi tanda kutip tunggal juga di-escape. Banyak codebase masih memberikan ENT_QUOTES, 'UTF-8' secara eksplisit agar pemanggilannya berperilaku sama di versi lama dan jelas bagi pembaca.

Sebaiknya htmlspecialchars dipakai saat input atau output?

Saat output. Simpan dan validasi nilai mentahnya, lalu escape tepat saat kamu mencetaknya ke HTML. Escape saat input menyimpan &lt; di database-mu, merusak panjang teks dan pencarian, serta menyebabkan escape ganda seperti &amp;lt;.

Apakah strip_tags cukup untuk mencegah XSS?

Tidak. strip_tags() menghapus tag, tetapi parameter tag yang diizinkan mempertahankan atributnya, jadi <b onclick="..."> tetap lolos, dan fungsi ini tidak berbuat apa-apa pada teks yang ditaruh di dalam atribut. Pakai htmlspecialchars() saat mencetak input pengguna.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai