htmlspecialchars($text) mengubah &, <, >, ", dan ' menjadi &, <, >, ", dan '. 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 <b> 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 < 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 ", 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 & pada escape pertama dan &amp; pada escape kedua, yang ditampilkan browser sebagai &. 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é. 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 mempertahankanonclick-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()atauurlencode()meng-encode nilainya;htmlspecialchars()lalu membuat&di antara parameter menjadi HTML yang valid. - Di JavaScript,
json_encode()menghasilkan nilai JS yang valid, danJSON_HEX_TAGmengubah<dan>menjadi\u003Cdan\u003E, sehingga</script>di dalam data tidak bisa menutup tag script. - Jangan pernah mencetak input pengguna ke
hreftanpa memeriksa skemanya:htmlspecialchars('javascript:alert(1)')tidak berubah dan tetap berjalan saat diklik. Terima hanya URLhttpdanhttps, 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 &, <, >, ", dan '. 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é. 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 < di database-mu, merusak panjang teks dan pencarian, serta menyebabkan escape ganda seperti &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.