web-dev-qa-db-ja.com

JavaScriptでHTMLエンティティをエスケープ解除しますか?

XML-RPCバックエンドと通信するJavascriptコードがいくつかあります。 XML-RPCは次の形式の文字列を返します。

<img src='myimage.jpg'>

ただし、Javascriptを使用して文字列をHTMLに挿入すると、文字どおりにレンダリングされます。画像が表示されず、文字列が文字通り表示されます。

<img src='myimage.jpg'>

私の推測では、HTMLはXML-RPCチャネルを介してエスケープされています。

Javascriptで文字列をエスケープ解除するにはどうすればよいですか?このページのテクニックを試してみましたが、失敗しました: http://paulschreiber.com/blog/2008/09/20/javascript-how-to-unescape-html-entities/

問題を診断する他の方法は何ですか?

137
Joseph Turian

EDIT:DOMParser APIを Wladimirが示唆する として使用する必要があります。投稿された関数にセキュリティ脆弱性が導入されたため、以前の回答を編集しました。

次のスニペットは、小さな修正を加えた古い回答のコードです。textareaの代わりにdivを使用すると、XSSの脆弱性が軽減されますが、IE9およびFirefoxでは依然として問題があります。

function htmlDecode(input){
  var e = document.createElement('textarea');
  e.innerHTML = input;
  // handle case of empty input
  return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}

htmlDecode("&lt;img src='myimage.jpg'&gt;"); 
// returns "<img src='myimage.jpg'>"

基本的に、プログラムでDOM要素を作成し、エンコードされたHTMLをそのinnerHTMLに割り当て、innerHTML挿入で作成されたテキストノードからnodeValueを取得します。要素を作成するだけで追加はしないため、サイトのHTMLは変更されません。

クロスブラウザ(古いブラウザを含む)で動作し、すべての HTML文字エンティティ を受け入れます。

編集:このコードの古いバージョンは、 ここではjsFiddleで (IEで表示)のように、空白の入力があるIEでは動作しませんでした。上記のバージョンはすべての入力で機能します。

更新:これは大きな文字列では機能せず、セキュリティ脆弱性も導入しています。コメントを参照してください。

160
CMS

ここで与えられるほとんどの答えには大きな欠点があります:変換しようとしている文字列が信頼されていない場合、 クロスサイトスクリプティング(XSS)の脆弱性 になります。 accepted answer の関数については、次のことを考慮してください。

htmlDecode("<img src='dummy' onerror='alert(/xss/)'>");

ここの文字列にはエスケープされていないHTMLタグが含まれているため、何もデコードする代わりに、htmlDecode関数は実際に文字列内で指定されたJavaScriptコードを実行します。

これは DOMParser を使用することで回避できます。これは すべての最新ブラウザー でサポートされています:

function htmlDecode(input)
{
  var doc = new DOMParser().parseFromString(input, "text/html");
  return doc.documentElement.textContent;
}

// This returns "<img src='myimage.jpg'>"
htmlDecode("&lt;img src='myimage.jpg'&gt;");

// This returns ""
htmlDecode("<img src='dummy' onerror='alert(/xss/)'>");

この関数は、副作用としてJavaScriptコードを実行しないことが保証されています。 HTMLタグは無視され、テキストコンテンツのみが返されます。

互換性メモDOMParserを使用したHTMLの解析には、少なくともChrome 30、Firefox 12、Operaが必要です_ 17、Internet Explorer 10、Safari 7.1またはMicrosoft Edge。そのため、サポートされていないすべてのブラウザーはEOLを過ぎており、2017年の時点でまだ世間に見られる唯一のブラウザーは、古いInternet ExplorerとSafariバージョンだけです(通常、これらはまだ気にするほど多くありません)。

295
Wladimir Palant

JQueryを使用している場合:

function htmlDecode(value){ 
  return $('<div/>').html(value).text(); 
}

それ以外の場合は、 Strictly Software's Encoder Object を使用します。これには優れたhtmlDecode()関数があります。

38
Chris Fulstow

トリックは、ブラウザのパワーを使用して特別なHTML文字をデコードすることですが、ブラウザが実際のhtmlであるかのように結果を実行することを許可しません...一度に。

function unescapeHtml(html) {
    var el = document.createElement('div');
    return html.replace(/\&[#0-9a-z]+;/gi, function (enc) {
        el.innerHTML = enc;
        return el.innerText
    });
}
7
Ben White

エスケープを解除したいHTMLが65536文字よりも長くない限り、CMSの答えは問題なく機能します。 Chromeでは、内側のHTMLが多くて65536の子ノードに分割され、それらを連結する必要があるためです。この関数は、非常に長い文字列でも機能します。

function unencodeHtmlContent(escapedHtml) {
  var elem = document.createElement('div');
  elem.innerHTML = escapedHtml;
  var result = '';
  // Chrome splits innerHTML into many child nodes, each one at most 65536.
  // Whereas FF creates just one single huge child node.
  for (var i = 0; i < elem.childNodes.length; ++i) {
    result = result + elem.childNodes[i].nodeValue;
  }
  return result;
}

詳細については、innerHTML max lengthについてのこの回答を参照してください。 https://stackoverflow.com/a/27545633/694469

5
KajMagnus

クリスの答えはナイスでエレガントですが、値がndefinedの場合は失敗します。単純な改善だけで確実になります:

function htmlDecode(value) {
   return (typeof value === 'undefined') ? '' : $('<div/>').html(value).text();
}
3
nerijus

あなたの質問への直接的な回答ではありませんが、RPCがその構造内の画像データ(例ではURL)を含む構造(XMLやJSONなど)を返す方が良いと思いませんか?

次に、javascriptで解析し、javascript自体を使用して<img>をビルドします。

RPCから受け取る構造は次のようになります。

{"img" : ["myimage.jpg", "myimage2.jpg"]}

外部ソースからのコードをページに挿入することはあまり安全ではないため、この方法の方が良いと思います。誰かがあなたのXML-RPCスクリプトをハイジャックし、あなたがそこに望まない何かを置くことを画像化します(いくつかのjavascriptさえ...)

3
kender

どういたしまして...メッセンジャーだけで...全額クレジットはourcodeworld.comにリンクされています。

window.htmlentities = {
        /**
         * Converts a string to its html characters completely.
         *
         * @param {String} str String with unescaped HTML characters
         **/
        encode : function(str) {
            var buf = [];

            for (var i=str.length-1;i>=0;i--) {
                buf.unshift(['&#', str[i].charCodeAt(), ';'].join(''));
            }

            return buf.join('');
        },
        /**
         * Converts an html characterSet into its original character.
         *
         * @param {String} str htmlSet entities
         **/
        decode : function(str) {
            return str.replace(/&#(\d+);/g, function(match, dec) {
                return String.fromCharCode(dec);
            });
        }
    };

完全なクレジット: https://ourcodeworld.com/articles/read/188/encode-and-decode-html-entities-using-pure-javascript

2
indospace.io

これは良いです:

String::decode = ->
   $('<textarea />').html(this).text()

つかいます:

"&lt;img src='myimage.jpg'&gt;".decode();

from: HTMLエンティティデコード

1

ここにある他のすべての答えには問題があります。

Document.createElement( 'div')メソッド(jQueryを使用するメソッドを含む)は、渡されたJavaScript(セキュリティの問題)を実行し、DOMParser.parseFromString()メソッドは空白を削除します。どちらも問題のない純粋なJavaScriptソリューションを次に示します。

function htmlDecode(html) {
    var textarea = document.createElement("textarea");
    html= html.replace(/\r/g, String.fromCharCode(0xe000)); // Replace "\r" with reserved unicode character.
    textarea.innerHTML = html;
    var result = textarea.value;
    return result.replace(new RegExp(String.fromCharCode(0xe000), 'g'), '\r');
}

TextAreaは、executig jsコードを回避するために特に使用されます。これらに合格します:

htmlDecode('&lt;&amp;&nbsp;&gt;'); // returns "<& >" with non-breaking space.
htmlDecode('  '); // returns "  "
htmlDecode('<img src="dummy" onerror="alert(\'xss\')">'); // Does not execute alert()
htmlDecode('\r\n') // returns "\r\n", doesn't lose the \r like other solutions.
0
Dwayne

私はこれを私のプロジェクトで使用します: その他の回答 に触発されましたが、追加の安全なパラメーターを使用すると、装飾された文字を扱うときに役立ちます

var decodeEntities=(function(){

    var el=document.createElement('div');
    return function(str, safeEscape){

        if(str && typeof str === 'string'){

            str=str.replace(/\</g, '&lt;');

            el.innerHTML=str;
            if(el.innerText){

                str=el.innerText;
                el.innerText='';
            }
            else if(el.textContent){

                str=el.textContent;
                el.textContent='';
            }

            if(safeEscape)
                str=str.replace(/\</g, '&lt;');
        }
        return str;
    }
})();

そして、それは次のように使用できます:

var label='safe <b> character &eacute;ntity</b>';
var safehtml='<div title="'+decodeEntities(label)+'">'+decodeEntities(label, true)+'</div>';
0
tmx976