HTML varlıkları olarak HTML etiketlerinden kaçmanın en hızlı yöntemi?


100

Ben yaptığını içerir bir Chrome uzantısı yazıyorum çok dizeleri sterilize: Aşağıdaki işin olabilir dönüştürerek HTML etiketleri içeren <, >ve &karşı &lt;, &gt;ve &amp;sırasıyla.

(Başka bir deyişle, PHP'dekiyle aynı htmlspecialchars(str, ENT_NOQUOTES)- çift tırnak karakterlerini dönüştürmeye gerçekten ihtiyaç olduğunu sanmıyorum.)

Bu şimdiye kadar bulduğum en hızlı işlev:

function safe_tags(str) {
    return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') ;
}

Ancak tek seferde birkaç bin dizgeyi geçmem gerektiğinde hala büyük bir gecikme var.

Herhangi biri bunu geliştirebilir mi? Çoğunlukla 10 ile 150 karakter arasındaki dizeler için, eğer bu bir fark yaratıyorsa.

(Aklıma gelen bir fikir, büyüktür işaretini kodlamakla uğraşmamaktı - bunda herhangi bir gerçek tehlike olur muydu?)


2
Neden? Bunu yapmak istediğiniz çoğu durumda, verileri DOM'a eklemek istersiniz; bu durumda, ondan çıkmayı unutup ondan bir textNode oluşturmanız gerekir.
Quentin

1
@David Dorward: Belki de POST verilerini sterilize etmek istedi ve sunucu verileri doğru bir şekilde dolaştırmıyor.
Lie Ryan

4
@Lie - öyleyse, çözüm "Pete aşkına, büyük bir XSS deliğiniz olduğu için sunucuyu düzeltin"
Quentin

2
@David Dorward: Durumun sunucu üzerinde kontrolü olmaması mümkündür. Son zamanlarda, üniversitemin web sitesinde sevmediğim birkaç şeyi geçici olarak çözmek için bir greasemonkey komut dosyası yazdığım bir durumla karşılaştım; POST verilerini javascript kullanarak kontrol edemediğim ve sterilize ettiğim bir sunucuda bir POST yapmak zorunda kaldım (ham veriler zengin bir metin kutusundan geldiğinden ve sunucuda gidiş-dönüş yapmayan html etiketleri yığınları olduğu için) . Web yöneticisi, web sitesini düzeltme isteğimi görmezden geliyordu, bu yüzden başka seçeneğim yoktu.
Lie Ryan

1
Bir div'de hata mesajı görüntülemem gereken bir kullanım durumum var. Hata mesajı HTML ve satırsonu içerebilir. HTML'den kaçmak ve yeni satırları <br> ile değiştirmek istiyorum. Ardından sonucu görüntülemek için bir div öğesine yerleştirin.
mozey

Yanıtlar:


85

Değiştirme işlemini gerçekleştirmek için bir geri arama işlevi iletmeyi deneyebilirsiniz:

var tagsToReplace = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;'
};

function replaceTag(tag) {
    return tagsToReplace[tag] || tag;
}

function safe_tags_replace(str) {
    return str.replace(/[&<>]/g, replaceTag);
}

İşte bir performans testi: http://jsperf.com/encode-html-entities , replaceişlevi tekrar tekrar çağırmakla ve Dmitrij tarafından önerilen DOM yöntemini kullanarak karşılaştırmak için.

Senin yolun daha hızlı görünüyor ...

Neden buna ihtiyacın var?


2
Kaçmaya gerek yok >.

6
Aslında, eğer çıkış yapılmış değeri bir html elemanının özniteliğine koyarsanız,> sembolünden çıkmanız gerekir. Aksi takdirde, o html öğesi için etiketi bozardı.
Zlatin Zlatev

1
Normal metinde kaçan karakterler nadirdir. Maksimum hızı önemsiyorsanız, yalnızca gerektiğinde değiştirmeyi aramak daha iyidir:if (/[<>&"]/.test(str) { ... }
Vitaly

3
@callum: Hayır. "Bir şeylerin ters gidebileceğini" düşündüğüm vakaları sıralamakla ilgilenmiyorum (en azından sizi incitecek beklenmedik / unutulmuş vakalar ve en azından bunu beklediğinizde). Standartlara göre kodlama ile ilgileniyorum (bu nedenle beklenmedik / unutulmuş durumlar tanımı gereği size zarar veremez ). Bunun ne kadar önemli olduğunu vurgulayamam. >HTML'de özel bir karakterdir, bu yüzden kaçın. Bu kadar basit. :)
Orbit'te Hafiflik Yarışları

4
@LightnessRacesinOrbit Bu konuyla ilgili çünkü soru mümkün olan en hızlı yöntemin ne olduğu. >Değiştirmeyi atlamak mümkünse , bu daha hızlı olur.
callum

105

İşte bunu yapmanın bir yolu:

var escape = document.createElement('textarea');
function escapeHTML(html) {
    escape.textContent = html;
    return escape.innerHTML;
}

function unescapeHTML(html) {
    escape.innerHTML = html;
    return escape.textContent;
}

İşte bir demo.


Demoyu yeniden tasarladı. İşte tam ekran versiyonu: jsfiddle.net/Daniel_Hug/qPUEX/show/light
Web_Designer

13
Nasıl / ne / neden emin değilim - ama bu dahice.
rob_james

4
Görünüşe göre, değişmez metinden kaçmak için TextArea öğesinin mevcut kodunu kullanıyor. Çok güzel, bence bu küçük numara başka bir yuva bulacak.
Ajax

3
@jazkat Bu işlevi kullanmıyorum. Kullandığım kaçış değişkeni, kendimi örnekte tanımlıyorum.
Web_Designer

2
ancak bu boşluk boşluğu kaybediyor mu?
Andrew

32

Martijn'in prototip işlevi olarak yöntemi:

String.prototype.escape = function() {
    var tagsToReplace = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;'
    };
    return this.replace(/[&<>]/g, function(tag) {
        return tagsToReplace[tag] || tag;
    });
};

var a = "<abc>";
var b = a.escape(); // "&lt;abc&gt;"

12
Buna ek olarak String, bu , genel olarak bir String için bir çıkış olmadığından, escapeHtml olmalıdır . Bu String.escapeHtmldoğru, ancak String.escape"ne için kaçış?" Sorusunu gündeme getiriyor.
Lawrence Dol

3
Evet iyi fikir. Çatışmaları önlemek için bugünlerde prototipi genişletmekten uzaklaştım.
Aram Kocharyan

1
Tarayıcınız Symbol desteğine sahipse, bunun yerine string-key ad alanını kirletmekten kaçınmak için bunu kullanabilirsiniz. var escape = new Sembol ("kaçış"); String.prototype [kaçış] = işlev () {...}; "metin" [kaçış] ();
Ajax

artı bir örnek için.
Timo


12

Daha da hızlı / daha kısa bir çözüm:

escaped = new Option(html).innerHTML

Bu, Option öğesinin bu türden otomatik olarak kaçış yapan bir yapıcıyı tuttuğu bazı garip JavaScript kalıntılarıyla ilgilidir.

Https://github.com/jasonmoo/t.js/blob/master/t.js'ye kredi verin


1
Düzgün tek satırlık ama normal ifadeden sonraki en yavaş yöntem . Ayrıca, spesifikasyona
ShortFuse

@ ShortFuse'un "en yavaş yöntem" bağlantısının sistemimin RAM'inin bitmesine neden olduğunu (~ 6 GB boş) ve firefox'un bellek tükenmeden hemen önce ayırmayı durdurduğunu ve bu nedenle sorun teşkil eden süreci öldürmek yerine linux'un orada oturup sizin yapmanıza izin verdiğini unutmayın. sert bir güç kapatma.
Luc

11

AngularJS kaynak kodunun ayrıca angular-sanitize.js içinde bir sürümü vardır .

var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
    // Match everything outside of normal chars and " (quote character)
    NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
/**
 * Escapes all potentially dangerous characters, so that the
 * resulting string can be safely inserted into attribute or
 * element text.
 * @param value
 * @returns {string} escaped text
 */
function encodeEntities(value) {
  return value.
    replace(/&/g, '&amp;').
    replace(SURROGATE_PAIR_REGEXP, function(value) {
      var hi = value.charCodeAt(0);
      var low = value.charCodeAt(1);
      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
    }).
    replace(NON_ALPHANUMERIC_REGEXP, function(value) {
      return '&#' + value.charCodeAt(0) + ';';
    }).
    replace(/</g, '&lt;').
    replace(/>/g, '&gt;');
}

1
Vay canına, bu alfanum dışı normal ifade yoğun. Sanmıyorum | ifadede olsa da gereklidir.
Ajax

9

Hepsi bir arada komut dosyası:

// HTML entities Encode/Decode

function htmlspecialchars(str) {
    var map = {
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        "\"": "&quot;",
        "'": "&#39;" // ' -> &apos; for XML only
    };
    return str.replace(/[&<>"']/g, function(m) { return map[m]; });
}
function htmlspecialchars_decode(str) {
    var map = {
        "&amp;": "&",
        "&lt;": "<",
        "&gt;": ">",
        "&quot;": "\"",
        "&#39;": "'"
    };
    return str.replace(/(&amp;|&lt;|&gt;|&quot;|&#39;)/g, function(m) { return map[m]; });
}
function htmlentities(str) {
    var textarea = document.createElement("textarea");
    textarea.innerHTML = str;
    return textarea.innerHTML;
}
function htmlentities_decode(str) {
    var textarea = document.createElement("textarea");
    textarea.innerHTML = str;
    return textarea.value;
}

http://pastebin.com/JGCVs0Ts


Olumsuz oy vermedim, ancak tüm normal ifade stili değiştirmeleri unicode'u kodlayamayacak ... Yani, yabancı dil kullanan herkes hayal kırıklığına uğrayacak. Yukarıda bahsedilen <textarea> hilesi gerçekten harika ve her şeyi hızlı ve güvenli bir şekilde hallediyor.
Ajax

Normal ifade, Latin olmayan bir dizi Unicode karakterle benim için iyi çalışıyor. Başka bir şey beklemiyorum. Bunun nasıl işe yaramayacağını düşünüyorsun? HTML varlıkları gerektiren tek baytlık kod sayfalarını mı düşünüyorsunuz? 3. ve 4. işlev bunun içindir ve açıkça 1. ve saniye için değildir. Farklılaşmayı seviyorum.
ygoe

@LonelyPixel Ondan bahsetmezseniz yorumunuzu göreceğini sanmıyorum ("Yalnızca bir ek kullanıcıya bildirimde bulunulabilir; gönderi sahibi her zaman bilgilendirilir")
baptx

Hedeflenen bildirimlerin var olduğunu bilmiyordum. @Ajax lütfen yukarıdaki yorumuma bakın.
ygoe

@LonelyPixel şimdi görüyorum. Bazı nedenlerden dolayı, bu yanıtta bir metin alanı stili değiştirme olduğunu düşünmedim. Aslında Mandarin gibi çift kod noktalı büyük unicode değerleri düşünüyordum. Demek istediğim, bir normal ifadeyi yeterince akıllı yapmak mümkün olabilirdi, ancak tarayıcı satıcılarının kullanabileceği kısayollara baktığınızda, metin alanının çok daha hızlı olacağına dair oldukça iyi hissederim (tamamen yetkin bir normal ifadeden). Birisi bu cevap için bir kıyaslama yaptı mı? Bir tane gördüğüme yemin ettim.
Ajax

2

function encode(r) {
  return r.replace(/[\x26\x0A\x3c\x3e\x22\x27]/g, function(r) {
	return "&#" + r.charCodeAt(0) + ";";
  });
}

test.value=encode('How to encode\nonly html tags &<>\'" nice & fast!');

/*
 \x26 is &ampersand (it has to be first),
 \x0A is newline,
 \x22 is ",
 \x27 is ',
 \x3c is <,
 \x3e is >
*/
<textarea id=test rows=11 cols=55>www.WHAK.com</textarea>


1

Hızdan tam olarak emin değilim, ancak basitlik arıyorsanız, lodash / alt çizgi kaçış işlevini kullanmanızı öneririm .


0

Martijn'in " mark " işlemeli tek işlevli yöntemi ( javascript'te kullanarak ):

function escapeHTML(html) {
    var fn=function(tag) {
        var charsToReplace = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&#34;'
        };
        return charsToReplace[tag] || tag;
    }
    return html.replace(/[&<>"]/g, fn);
}

0

Yığına ekleyeceğim XMLSerializer. Herhangi bir nesne önbelleği kullanmadan en hızlı sonucu sağlar (serileştiricide veya Metin düğümünde değil).

function serializeTextNode(text) {
  return new XMLSerializer().serializeToString(document.createTextNode(text));
}

Eklenen bonus, metin düğümlerinden farklı şekilde serileştirilmiş öznitelikleri desteklemesidir:

function serializeAttributeValue(value) {
  const attr = document.createAttribute('a');
  attr.value = value;
  return new XMLSerializer().serializeToString(attr);
}

Sen hem, aslında spec kontrol ederek yerine gelen görebilirsiniz metin düğümleri ve için öznitelik değerleri . Tam dokümantasyonda daha fazla düğüm türü vardır, ancak kavram aynıdır.

Performans gelince, önbelleğe alınmadığında en hızlısı. Önbelleğe innerHTMLalmaya izin verdiğinizde, alt Metin düğümüne sahip bir HTMLElement'i çağırmak en hızlısıdır. Regex en yavaş olacaktır (diğer yorumların da kanıtladığı gibi). Elbette, XMLSerializer diğer tarayıcılarda daha hızlı olabilir, ancak benim (sınırlı) testimde a innerHTMLen hızlısıdır.


En hızlı tek hat:

new XMLSerializer().serializeToString(document.createTextNode(text));

Önbelleğe alma ile en hızlı:

const cachedElementParent = document.createElement('div');
const cachedChildTextNode = document.createTextNode('');
cachedElementParent.appendChild(cachedChildTextNode);

function serializeTextNode(text) {
  cachedChildTextNode.nodeValue = text;
  return cachedElementParent.innerHTML;
}

https://jsperf.com/htmlentityencode/1


-3

Gösteriye biraz geç kaldınız , ancak encodeURIComponent () ve decodeURIComponent () kullanmanın nesi yanlış ?


1
Bunlar tamamen ilgisiz bir şey yapıyor
callum

1
Belki de şimdiye kadar duyduğum "tamamen" kelimesinin en büyük kötüye kullanımı. Örneğin, ana konu sorusuyla ilgili olarak, html etiketlerinden bağımsız olarak bir html dizesinin kodunu çözmek için (açıkça bir tür depolama nedeni için) kullanılabilir ve daha sonra gerektiğinde ve gerektiğinde yeniden html olarak kolayca kodlanabilir.
suncat100
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.