HTMLエンティティとしてHTMLタグをエスケープする最速の方法?


98

私がやっ含まChromeの拡張機能書いている多くの文字列をサニタイズ:次の仕事のがあります変換することによって、HTMLタグが含まれているが<>&&lt;&gt;そして&amp;それぞれ。

(言い換えれば、PHPと同じhtmlspecialchars(str, ENT_NOQUOTES)です。二重引用符の文字を変換する必要は実際にはないと思います。)

これは私がこれまでに見つけた最速の関数です:

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

しかし、一度に数千の文字列を実行しなければならない場合には、まだ大きな遅れがあります。

誰でもこれを改善できますか?それが違いを生む場合、それは主に10〜150文字の文字列用です。

(私が持っていたアイデアの1つは、大なり記号をエンコードすることではありませんでした。これには本当に危険があるのでしょうか?)


2
どうして?これを行うほとんどの場合、データをDOMに挿入します。その場合、エスケープを忘れて、そこからtextNodeを作成する必要があります。
Quentin、

1
@David Dorward:おそらく彼はPOSTデータをサニタイズしたかったので、サーバーはデータを正しくラウンドトリップしません。
リーライアン

4
@Lie —その場合、解決策は「ピートのために、大きなXSSホールがあるのでサーバーを修正する」
Quentin

2
@David Dorward:彼がサーバーを制御していない可能性があります。私は最近、大学のWebサイトで気に入らないいくつかの問題を回避するためにgreasemonkeyスクリプトを作成しているような状況に陥っています。私は制御できないサーバーでPOSTを実行する必要があり、JavaScriptを使用してPOSTデータをサニタイズしました(生データはリッチテキストボックスから取得されるため、サーバー上で往復しないhtmlタグのヒープがあるため) 。ウェブ管理者は、彼らがウェブサイトを修正するという私のリクエストを無視していたので、他に選択肢はありませんでした。
リーライアン

1
divにエラーメッセージを表示する必要があるユースケースがあります。エラーメッセージには、HTMLと改行を含めることができます。HTMLをエスケープして、改行を<br>に置き換えます。次に、結果を表示用のdivに入れます。
mozey 2013

回答:


83

コールバック関数を渡して置換を実行してみることができます。

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

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

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

これはパフォーマンステストです。http//jsperf.com/encode-html-entitiesは、replace関数を繰り返し呼び出して、Dmitrijによって提案されたDOMメソッドを使用する場合と比較します。

あなたの方法はより速く見える...

なぜそれが必要なのですか?


2
脱出する必要はありません>

6
実際、エスケープされた値をhtml要素の属性に入れる場合は、>記号をエスケープする必要があります。そうしないと、そのhtml要素のタグが壊れます。
Zlatin Zlatev 2013年

1
通常のテキストでは、エスケープ文字はまれです。あなたは最高速度を心配している場合には、必要なときだけ置き換えるコールする方が良いでしょう:if (/[<>&"]/.test(str) { ... }
ヴィタリー

3
@callum:いいえ。「何かがうまくいかない可能性がある」と思うケースを列挙することには興味がありません(特に、あなたを傷つけるのは予期せぬ/忘れられたケースであり、少なくともそれを期待しないとき)。私は標準へのコーディングに興味があります(したがって、予期しない/忘れられたケースが定義によってあなた傷つけることはありません)。これがいかに重要であるかを強調することはできません。>HTMLの特殊文字なので、エスケープします。そのような単純な。:)
オービットのライトネスレース

4
@LightnessRacesinOrbit問題は、可能な最速の方法が何であるかということに関係があります。>置き換えをスキップすることが可能であれば、それはより速くなります。
カラム

103

これを行う方法の1つを次に示します。

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

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

こちらがデモです。


デモを再設計しました。これがフルスクリーンバージョンです:jsfiddle.net/Daniel_Hug/qPUEX/show/light
Web_Designer

13
方法/内容/理由がわからない-しかし、これは天才です。
rob_james 2014年

4
TextArea要素の既存のコードを利用してリテラルテキストをエスケープしているようです。とてもいいです、この小さなトリックが別の家を見つけると思います。
Ajax

3
@jazkatその関数は使っていません。私が使用するエスケープ変数は、この例で自分自身を定義しています。
Web_Designer 2017

2
しかし、これにより空白などが失われます
Andrew

31

プロトタイプ関数としてのマルティンの方法:

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
Stringこのように追加すると、一般的に文字列のエスケープではないため、escapeHtmlになります。それはString.escapeHtml正しいですがString.escape、「何のために逃げるのか」という疑問を投げかけます。
ローレンスドル、2014年

3
そうだね。衝突を避けるために、私は最近、プロトタイプを拡張することから離れました。
Aram Kocharyan

1
ブラウザーがSymbolをサポートしている場合は、代わりにそれを使用して、文字列キーの名前空間の汚染を回避できます。var escape = new Symbol( "escape"); String.prototype [escape] = function(){...}; "テキスト" [エスケープ]();
Ajax

12

より迅速で短いソリューションは次のとおりです。

escaped = new Option(html).innerHTML

これはJavaScriptの奇妙な痕跡に関連しており、Option要素はこの種のエスケープを自動的に行うコンストラクタを保持します。

https://github.com/jasonmoo/t.js/blob/master/t.jsへのクレジット


1
きちんとしたワンライナーですが、正規表現の後に最も遅い方法です。また、仕様
ShortFuse

@ShortFuseの「最も遅いメソッド」リンクにより、システムのRAMが不足し(最大6GBが空き)、Firefoxがメモリ不足になる直前に割り当てを停止しているように見えるので、問題のプロセスを強制終了する代わりに、Linuxがそこに座って実行させます。ハード電源オフ。
Luc

11

AngularJSソースコードには、angular-sanitize.js内のバージョンもあります。

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
うわー、その非アルファナム正規表現は激しいです。私は考えていません| でも式は必要です。
Ajax


9

オールインワンスクリプト:

// 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


私は反対票を投じなかったが、すべての正規表現スタイルの置換はユニコードのエンコードに失敗します...したがって、外国語を使用している人は誰もがっかりするでしょう。上記の<textarea>トリックは非常に優れており、すべてを迅速かつ安全に処理します。
Ajax

正規表現は、ラテン文字以外のUnicode文字の数でうまく機能します。他には何も期待していません。これはうまくいかないと思いますか?HTMLエンティティを必要とするシングルバイトコードページを考えていますか?これが3番目と4番目の関数の目的であり、明示的に1番目と2番目の関数ではありません。差別化が好きです。
ygoe 2016

@LonelyPixelあなたが彼に言及しなかった場合、彼はあなたのコメントを見ることはないと思います(「通知できる追加ユーザーは1人だけです。投稿の所有者には常に通知されます」)
baptx

対象となる通知が存在することをまったく知りませんでした。@Ajax上記のコメントをご覧ください。
ygoe 2016年

@LonelyPixel私は今見ます。何らかの理由で、この回答にtextareaスタイルの置換があるとは思いませんでした。実際、マンダリンのように、二重のコードポイントの大きなユニコード値を考えていました。つまり、正規表現を十分にスマートにすることは可能ですが、ブラウザーベンダーが取ることができるショートカットを見ると、(完全に有能な正規表現よりも)textareaの方がはるかに高速であることを確信しています。誰かがこの回答にベンチマークを投稿しましたか?見たことあると誓った。
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>



0

ハンドリングを持つ単一の関数としてマルタインの方法(マークをJavaScriptで使用):

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

0

XMLSerializerパイルに追加します。オブジェクトキャッシュを使用せずに(シリアライザ上でもテキストノード上でも)最速の結果を提供します。

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

追加のボーナスは、テキストノードとは異なる方法でシリアル化される属性をサポートすることです。

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

テキストノード属性値の両方の仕様を確認することで、実際に置き換えられる内容を確認できます。完全なドキュメントにはより多くのノードタイプがありますが、概念は同じです。

パフォーマンスに関しては、キャッシュされていないときが最速です。キャッシュを許可する場合innerHTML、子のTextノードを持つHTMLElementの呼び出しが最も高速です。正規表現は最も遅くなります(他のコメントで証明されています)。もちろん、他のブラウザではXMLSerializerの方が高速かもしれませんが、私の(制限された)テストでinnerHTMLは、a が最速です。


最速の1行:

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

キャッシングで最速:

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

少し遅れて表示されますが、encodeURIComponent()およびdecodeURIComponent()の使用の何が問題になっていますか?


1
それらは完全に無関係な何かをします
カルム

1
おそらく、私が聞いたことのある「完全に」という言葉の最大の乱用。たとえば、メイントピックの質問に関連して、htmlタグに関係なく、html文字列を(明らかに何らかのストレージ上の理由で)デコードし、必要に応じて簡単に再度htmlにエンコードして戻すことができます。
suncat100 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.