HtmlSpecialChars setara dalam Javascript?

167

Rupanya, ini lebih sulit ditemukan daripada yang saya kira. Dan itu bahkan sangat sederhana ...

Apakah ada fungsi yang setara dengan htmlspecialchars PHP yang dibangun ke dalam Javascript? Saya tahu ini cukup mudah untuk diimplementasikan sendiri, tetapi menggunakan fungsi bawaan, jika tersedia, hanya lebih baik.

Bagi mereka yang tidak terbiasa dengan PHP, htmlspecialchars menerjemahkan hal-hal seperti <htmltag/>ke&lt;htmltag/&gt;

Saya tahu itu escape()dan encodeURI()tidak bekerja dengan cara ini.

Bart van Heukelom
sumber
php punya beberapa alat yang sangat bagus, var_dump, print_r, htmlspecialchars dll. Sayangnya saya curiga tidak sama dengan js. Peringatan js sangat buruk. Cara cepat untuk melihat bahwa beberapa string yang tak terduga (dan tidak terlihat dalam kotak peringatan) akan datang, adalah dengan mengingatkan panjang string, bukan stringnya.
Melsi
Kemungkinan duplikat untuk
menghindari
Lihat stackoverflow.com/a/12034334/8804293 , ia memiliki jawaban yang bagus
Elijah Mock

Jawaban:

330

Ada masalah dengan kode solusi Anda - itu hanya akan luput dari kemunculan pertama setiap karakter khusus. Sebagai contoh:

escapeHtml('Kip\'s <b>evil</b> "test" code\'s here');
Actual:   Kip&#039;s &lt;b&gt;evil</b> &quot;test" code's here
Expected: Kip&#039;s &lt;b&gt;evil&lt;/b&gt; &quot;test&quot; code&#039;s here

Berikut adalah kode yang berfungsi dengan baik:

function escapeHtml(text) {
  return text
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#039;");
}

Memperbarui

Kode berikut akan menghasilkan hasil yang identik dengan di atas, tetapi kinerjanya lebih baik, terutama pada blok teks yang besar (terima kasih jbo5112 ).

function escapeHtml(text) {
  var map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  };
  
  return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
Tidur
sumber
5
Yang menyenangkan tentang fungsi ini adalah ia bekerja di node.js yang tidak memiliki dom secara default
booyaa
6
Lebih cepat menggunakan fungsi ganti dan pemetaan tunggal, dan skala ganti tunggal jauh lebih baik. ( jsperf.com/escape-html-special-chars/11 )
jbo5112
1
@ titik bagus jbo5112, saya tidak menyadari JS memungkinkan panggilan balik untuk penggantian. Kode ini lebih mudah dimengerti, dan saya ragu bahwa mencukur beberapa milidetik dari escapeHtml () akan membuat perbedaan kecuali Anda memanggilnya ratusan kali berturut-turut karena suatu alasan.
Kip
Ini akan mendistorsi URL dalam teks yang membuatnya tidak dapat digunakan untuk plugin seperti Autolinker.js . Apakah ada cara bagaimana mendekati ini?
Radek Matěj
4
@ RadekMatěj Bahkan dalam kasus itu sangat valid (lebih baik saya berpendapat) untuk kedua ampersand yang akan dikodekan ketika digunakan dalam dokumen HTML. Saya masih menganggapnya sebagai bug dengan plugin.
Kip
31

Itu Pengodean HTML. Tidak ada fungsi javascript asli untuk melakukan itu, tetapi Anda dapat google dan mendapatkan beberapa yang dilakukan dengan baik.

Misalnya http://sanzon.wordpress.com/2008/05/01/neat-little-html-encoding-trick-in-javascript/

EDIT:
Ini yang saya uji:

var div = document.createElement('div');
  var text = document.createTextNode('<htmltag/>');
  div.appendChild(text);
  console.log(div.innerHTML);

Keluaran: &lt;htmltag/&gt;

baik w
sumber
Sayang sekali, saya hanya harus menggunakan fungsi kustom saja.
Bart van Heukelom
Anda dapat mencoba metode di tautan yang saya sertakan dalam posting saya. Konsep yang cukup rapi memang.
okw
@ okw: Ok, pertama Anda ditautkan ke ini: yuki-onna.co.uk/html/encode.html yang melakukan persis apa yang encodeURIComponentdilakukan dan tidak sama sekali dengan apa yang diminta OP. Jadi bisakah Anda mengedit tolong? Sepertinya saya tidak bisa membatalkan -1 saya.
Crescent Fresh
Yah, kode halaman itu terlihat logis tetapi saya tidak mengujinya. Tautan baru itu berfungsi, saya sudah memverifikasi sendiri. Saya sudah memperbarui pos beberapa waktu lalu.
okw
@BeauCielBleu: Tidak. Satu-satunya simpul yang dibuat adalah divelemen tunggal dan simpul teks. Membuat simpul teks dengan teks `<img src = bogus onerror = alert (1337)>` hanya akan membuat simpul teks, bukan imgelemen.
Tim Down
26

Layak dibaca: http://bigdingus.com/2007/12/29/html-escaping-in-javascript/

escapeHTML: (function() {
 var MAP = {
   '&': '&amp;',
   '<': '&lt;',
   '>': '&gt;',
   '"': '&#34;',
   "'": '&#39;'
 };
  var repl = function(c) { return MAP[c]; };
  return function(s) {
    return s.replace(/[&<>'"]/g, repl);
  };
})()

Catatan : Hanya jalankan ini sekali. Dan jangan jalankan pada string yang sudah dikodekan misalnya &amp;menjadi&amp;amp;

Chris Jacob
sumber
3
Ini harus menjadi jawaban yang diterima dan dipilih tertinggi. Saya tidak yakin mengapa itu tidak memiliki suara. Ini adalah pembandingan sebagai yang tercepat dengan hasil pencarian panjang (326KB Google) dan string input pendek pada jsperf ( jsperf.com/escape-html-special-chars/11 ). Harap beri suara ini.
jbo5112
Apa bedanya jawaban yang mendapat suara tertinggi ini? Mengapa fungsi dalam tambahan? Penjelasan dapat membantu pengguna lebih memahami
Kosem
19

Dengan jQuery bisa seperti ini:

var escapedValue = $('<div/>').text(value).html();

Dari pertanyaan terkait Melarikan string HTML dengan jQuery

Seperti yang disebutkan dalam komentar, kutipan ganda dan kutipan tunggal dibiarkan apa adanya untuk implementasi ini. Itu berarti solusi ini tidak boleh digunakan jika Anda perlu membuat atribut elemen sebagai string html mentah.

Alexander Yanovets
sumber
2
tahu jika ada overhead untuk ini - menambahkan objek dummy ke DOM?
Kip
dan apakah ada keuntungan lain (katakanlah, jika Anda memiliki karakter unik atau sesuatu)?
Kip
4
Sesuatu yang saya temukan dengan ini: tanda kutip ganda dan tanda kutip tunggal dibiarkan apa adanya. Itu membuat ini bermasalah jika Anda ingin menggunakannya dalam nilai atribut.
Kip
1
Untuk potongan teks kecil, ini membutuhkan waktu 30x selama menjalankan semua penggantian. Meskipun skala lebih baik. Dengan sesuatu yang sama besarnya dengan halaman hasil pencarian Google (326KB), ini 25-30% lebih cepat daripada yang menggantikan atau melakukan ini dalam javascript lurus. Namun, mereka semua secara konsisten kehilangan fungsi ganti tunggal dan pemetaan.
jbo5112
4
bagaimana orang memberikan suara pada jawaban ini: answer has jquery: +1 - TIDAK lepas dari tanda kutip tunggal dan ganda: ummmm .. (menggaruk kepala) .. +1. <!-- Caps rage begin --> Jawaban ini harus memiliki skor NEGATIF ​​karena TIDAK BAHKAN DATANG DEKAT DENGAN JAWABAN PERTANYAAN "HtmlSpecialChars equivalen". <!-- Caps rage end -->itu-tidak-tidak-melarikan diri-mengutip-yesus-krist-dan-dewa-lainnya. OMG Anda membuat orang jquery.
Sharky
19

Berikut adalah fungsi untuk keluar dari HTML:

function escapeHtml(str)
{
    var map =
    {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#039;'
    };
    return str.replace(/[&<>"']/g, function(m) {return map[m];});
}

Dan untuk memecahkan kode:

function decodeHtml(str)
{
    var map =
    {
        '&amp;': '&',
        '&lt;': '<',
        '&gt;': '>',
        '&quot;': '"',
        '&#039;': "'"
    };
    return str.replace(/&amp;|&lt;|&gt;|&quot;|&#039;/g, function(m) {return map[m];});
}
Dan Bray
sumber
6

Underscore.js menyediakan fungsi untuk ini:

_.escape(string)

Mengosongkan string untuk dimasukkan ke dalam HTML, menggantikan karakter &, <,>, ", dan '.

http://underscorejs.org/#escape

Ini bukan fungsi Javascript bawaan, tetapi jika Anda sudah menggunakan Underscore itu adalah alternatif yang lebih baik daripada menulis fungsi Anda sendiri jika string Anda untuk mengonversi tidak terlalu besar.

mer10z_tech
sumber
5

Namun hal lain yang harus dilakukan adalah melupakan semua pemetaan karakter sekaligus dan alih-alih mengubah semua karakter yang tidak diinginkan ke masing-masing referensi karakter numeriknya, misalnya:

function escapeHtml(raw) {
    return raw.replace(/[&<>"']/g, function onReplace(match) {
        return '&#' + match.charCodeAt(0) + ';';
    });
}

Perhatikan bahwa RegEx yang ditentukan hanya menangani karakter spesifik yang OP ingin hindari tetapi, tergantung pada konteks bahwa HTML yang lolos akan digunakan, karakter ini mungkin tidak cukup. Artikel Ryan Grove Ada lebih banyak hal untuk menghindari HTML daripada &, <,>, dan " adalah bacaan yang bagus untuk topik ini. Dan tergantung pada konteks Anda, RegEx berikut mungkin sangat diperlukan untuk menghindari injeksi XSS:

var regex = /[&<>"'` !@$%()=+{}[\]]/g
Fredric
sumber
3
String.prototype.escapeHTML = function() {
        return this.replace(/&/g, "&amp;")
                   .replace(/</g, "&lt;")
                   .replace(/>/g, "&gt;")
                   .replace(/"/g, "&quot;")
                   .replace(/'/g, "&#039;");
    }

Sampel :

var toto = "test<br>";
alert(toto.escapeHTML());
patrick
sumber
3

Kemungkinannya adalah Anda tidak membutuhkan fungsi seperti itu. Karena kode Anda sudah ada di browser *, Anda dapat mengakses DOM secara langsung alih-alih menghasilkan dan menyandikan HTML yang harus diterjemahkan kembali oleh peramban agar dapat digunakan.

Gunakan innerTextproperti untuk menyisipkan teks biasa ke DOM dengan aman dan jauh lebih cepat daripada menggunakan fungsi melarikan diri apa pun yang disajikan. Bahkan lebih cepat daripada menetapkan string preencoded statis innerHTML.

Gunakan classListuntuk mengedit kelas, datasetuntuk mengatur data-atribut dan setAttributeuntuk orang lain.

Semua ini akan menangani pelarian untuk Anda. Lebih tepatnya, tidak ada pelolosan yang diperlukan dan tidak ada pengkodean yang dilakukan di bawah **, karena Anda bekerja di sekitar HTML, representasi tekstual dari DOM.

// use existing element
var author = 'John "Superman" Doe <[email protected]>';
var el = document.getElementById('first');
el.dataset.author = author;
el.textContent = 'Author: '+author;

// or create a new element
var a = document.createElement('a');
a.classList.add('important');
a.href = '/search?q=term+"exact"&n=50';
a.textContent = 'Search for "exact" term';
document.body.appendChild(a);

// actual HTML code
console.log(el.outerHTML);
console.log(a.outerHTML);
.important { color: red; }
<div id="first"></div>

* Jawaban ini tidak ditujukan untuk pengguna JavaScript sisi-server (Node.js, dll. )

** Kecuali Anda secara eksplisit mengubahnya menjadi HTML aktual setelahnya. Misalnya dengan mengakses innerHTML- inilah yang terjadi ketika Anda menjalankan yang $('<div/>').text(value).html();disarankan dalam jawaban lain. Jadi, jika tujuan akhir Anda adalah memasukkan beberapa data ke dalam dokumen, dengan melakukannya dengan cara ini Anda akan melakukan pekerjaan dua kali. Anda juga dapat melihat bahwa dalam HTML yang dihasilkan tidak semuanya dikodekan, hanya minimum yang diperlukan agar valid. Ini dilakukan tergantung konteks, itu sebabnya metode jQuery ini tidak menyandikan tanda kutip dan karenanya tidak boleh digunakan sebagai escaper tujuan umum. Pelarian kutipan diperlukan ketika Anda membuat HTML sebagai string dengan data yang tidak tepercaya atau berisi kutipan di tempat nilai atribut. Jika Anda menggunakan DOM API, Anda tidak perlu repot melarikan diri sama sekali.

pengguna
sumber
Terima kasih untuk ini! Saya telah menghabiskan waktu lama untuk mencari solusi sederhana. Satu hal penting yang saya temukan adalah bahwa jika teks Anda mengandung baris baru, maka Anda harus menggantinya dengan jeda baris HTML (seperti el.textContent = str; el.innerHTML = el.innerHTML.replace(/\n/g, '<br>')), atau mengatur white-spaceproperti CSS ke preataupre-wrap
stellatedHexahedron
@stellatedHexahedron, terima kasih telah mengangkat masalah ini. Aku sudah berubah jawaban saya untuk merekomendasikan innerTextbukan textContent. Meskipun sedikit lebih lambat dan memiliki beberapa perbedaan lain ketika membaca properti, itu lebih intuitif karena ia melakukan <br>penggantian secara otomatis ketika menetapkan untuk itu.
pengguna
2

Untuk pengguna Node.JS (atau pengguna yang menggunakan runtime Jade di browser), Anda dapat menggunakan fungsi escape Jade.

require('jade').runtime.escape(...);

Tidak masuk akal untuk menulisnya sendiri jika orang lain memeliharanya. :)

BMiner
sumber
1

Saya sedikit menguraikan jawaban okw.

Anda dapat menggunakan fungsi DOM browser untuk itu.

var utils = {
    dummy: document.createElement('div'),
    escapeHTML: function(s) {
        this.dummy.textContent = s
        return this.dummy.innerHTML
    }
}

utils.escapeHTML('<escapeThis>&')

Ini kembali &lt;escapeThis&gt;&amp;

Ini menggunakan fungsi standar createElementuntuk membuat elemen yang tidak terlihat, kemudian menggunakan fungsi textContentuntuk mengatur string apa pun sebagai kontennya dan kemudian innerHTMLuntuk mendapatkan konten dalam representasi HTML-nya.

Jonas Eberle
sumber
0
function htmlspecialchars(str) {
 if (typeof(str) == "string") {
  str = str.replace(/&/g, "&amp;"); /* must do &amp; first */
  str = str.replace(/"/g, "&quot;");
  str = str.replace(/'/g, "&#039;");
  str = str.replace(/</g, "&lt;");
  str = str.replace(/>/g, "&gt;");
  }
 return str;
 }

sumber
0

Semoga ini memenangkan perlombaan karena kinerjanya dan yang paling penting bukan logika berantai menggunakan .replace ('&', '&'). Ganti ('<', '<') ...

var mapObj = {
   '&':"&amp;",
   '<':"&lt;",
   '>':"&gt;",
   '"':"&quot;",
   '\'':"&#039;"
};
var re = new RegExp(Object.keys(mapObj).join("|"),"gi");

function escapeHtml(str) 
{   
    return str.replace(re, function(matched)
    {
        return mapObj[matched.toLowerCase()];
    });
}

console.log('<script type="text/javascript">alert('Hello World');</script>');
console.log(escapeHtml('<script type="text/javascript">alert('Hello World');</script>'));
Airy
sumber
0

Yang dibalik:

function decodeHtml(text) {
    return text
        .replace(/&amp;/g, '&')
        .replace(/&lt;/ , '<')
        .replace(/&gt;/, '>')
        .replace(/&quot;/g,'"')
        .replace(/&#039;/g,"'");
}
Gleb Dolzikov
sumber
Pertanyaannya bukan menanyakan bagaimana cara mendekodekan entitas. Ini berlawanan dengan apa yang ditanyakan.
Quentin
Ini hanya akan menggantikan instance pertama&lt; dan &gr;dalam sebuah string.
Quentin
Ini hanya akan men-decode lima karakter yang (di luar dokumen non-Unicode) harus diloloskan, tidak akan men-decode karakter yang mungkin lolos.
Quentin
Ini tidak memperhitungkan aturan ketika titik koma adalah opsional.
Quentin
Jika HTML mengatakan:, To write a greater than sign in HTML type &amp;gt;itu akan ditampilkan secara salah, >bukan&gt;
Quentin
0

OWASP merekomendasikan bahwa "[e] kecuali untuk karakter alfanumerik, [Anda harus] keluar dari semua karakter dengan nilai ASCII kurang dari 256 dengan&#xHH; format (atau entitas bernama jika tersedia) untuk mencegah beralih dari atribut [an]."

Jadi, inilah fungsi yang melakukan itu, dengan contoh penggunaan:

function escapeHTML(unsafe) {
  return unsafe.replace(
    /[\u0000-\u002F]|[\u003A-\u0040]|[\u005B-\u00FF]/g,
    c => '&#' + ('000' + c.charCodeAt(0)).substr(-4, 4) + ';'
  )
}
document.querySelector('div').innerHTML =
  '<span class=' +
  escapeHTML('this should break it! " | / % * + , - / ; < = > ^') +
  '>' +
  escapeHTML('<script>alert("inspect the attributes")\u003C/script>') +
  '</span>'
<div></div>

ADJenks
sumber
-1
function htmlEscape(str){
    return str.replace(/[&<>'"]/g,x=>'&#'+x.charCodeAt(0)+';')
}

Solusi ini menggunakan kode numerik karakter, misalnya <diganti oleh&#60; .

Meskipun kinerjanya sedikit lebih buruk daripada solusi menggunakan peta , ia memiliki keuntungan:

  • Tidak bergantung pada perpustakaan atau DOM
  • Cukup mudah diingat (Anda tidak perlu mengingat 5 karakter HTML escape)
  • Kode kecil
  • Cukup cepat (masih lebih cepat dari 5 ganti rantai)
pengguna202729
sumber