デコード& JavaScriptに戻る


229

次のような文字列があります

var str = 'One & two & three';

WebサーバーによってHTMLにレンダリングされます。それらの文字列を

'One & two & three'

現在、それは私がやっていることです(jQueryの助けを借りて):

$(document.createElement('div')).html('{{ driver.person.name }}').text()

しかし、私はそれを間違っていると不安を感じています。私が試してみました

unescape("&")

しかし、機能していないようで、decodeURI / decodeURIComponentも機能しません。

他にもっとネイティブでエレガントな方法がありますか?


この記事に含まれる膨大な機能が正常に動作するようだ:blogs.msdn.com/b/aoakley/archive/2003/11/12/49645.aspxは、私はそれが最も賢いソリューションが、作品だとは思いません。
Matias

1
HTMLエンティティを含む文字列はescapedまたはURIエンコードされた文字列とは異なるため、これらの関数は機能しません。
マルセルコーペル

1
@Matiasは、その関数が2003年に作成されて以来、新しい名前付きエンティティがHTMLに追加されている(たとえば、HTML 5仕様を介して)ことに注意してください𝕫。たとえば、は認識しません。これは、進化する仕様の問題です。したがって、それを解決するために実際に保守されているツールを選択する必要があります。
Mark Amery 2017

1
@MarkAmeryはい、私は完全に同意します!数年後にこの質問に戻ってくるのはいい経験です、ありがとう!
マティアス2017

回答:


104

JavaScriptからHTML(テキストなど)を解釈するためのより近代的なオプションは、DOMParserAPIのHTMLサポートです(ここMDNを参照)。これにより、ブラウザのネイティブHTMLパーサーを使用して、文字列をHTMLドキュメントに変換できます。2014年後半以降、すべての主要ブラウザの新しいバージョンでサポートされています。

一部のテキストコンテンツをデコードするだけの場合は、それをドキュメント本文の唯一のコンテンツとして配置し、ドキュメントを解析して、そのコンテンツを引き出し.body.textContentます。

var encodedStr = 'hello & world';

var parser = new DOMParser;
var dom = parser.parseFromString(
    '<!doctype html><body>' + encodedStr,
    'text/html');
var decodedString = dom.body.textContent;

console.log(decodedString);

私たちはで見ることができるためにドラフト仕様DOMParser JavaScriptが解析された文書が使用できないため、我々は、セキュリティ上の懸念なしに、このテキスト変換を行うことができますので、。

parseFromString(str, type)この方法は、に応じて、これらの手順を実行する必要があります種類

  • "text/html"

    strをで解析しHTML parser、新しく作成されたを返しDocumentます。

    スクリプトフラグは「無効」に設定する必要があります。

    注意

    script要素は実行不可能とnoscriptマークされ、コンテンツはマークアップとして解析されます。

これはこの質問の範囲を超えていますが、解析されたDOMノード自体(テキストコンテンツだけでなく)を取得してライブドキュメントDOMに移動する場合、スクリプトが再度有効になる可能性があることに注意してください。セキュリティ上の懸念があります。まだ調べていないので注意してください。


5
NodeJの代替手段はありますか?
coderInrRain 2017

284

エンコードされたすべてのHTMLエンティティをデコードする必要があり&amp;ますか?

処理する必要があるだけなら、&amp;これを行うことができます:

var decoded = encoded.replace(/&amp;/g, '&');

すべてのHTMLエンティティをデコードする必要がある場合は、jQueryなしで実行できます。

var elem = document.createElement('textarea');
elem.innerHTML = encoded;
var decoded = elem.value;

以下のマークのコメントに注意してください。この回答の以前のバージョンのセキュリティホールを強調し、XSSの潜在的な脆弱性を軽減するためでtextareaはなく、使用することdivを推奨しています。これらの脆弱性は、jQueryまたはプレーンJavaScriptを使用しているかどうかに関係なく存在します。


16
注意してください!これは潜在的に安全ではありません。その場合encoded='<img src="bla" onerror="alert(1)">'、上記のスニペットはアラートを表示します。つまり、エンコードされたテキストがユーザー入力からのものである場合、このスニペットでデコードすると、XSSの脆弱性が生じる可能性があります。
マークアメリー2015

@MarkAmery Iないセキュリティの専門家が、あなたへの即時セットdivの場合のように見えるnullテキストを取得した後は、IMGでアラートが発射されていない- jsfiddle.net/Mottie/gaBeb/128を
Mottie

4
@Mottieは、どのブラウザで動作したかを確認しますが、alert(1)それでもOS XのChromeで起動します。このハッキングの安全なバリアントが必要な場合は、を使用してtextareaください。
Mark Amery

単純な正規表現の+1は、1種類のhtmlエンティティの代替を置き換えます。たとえば、pythonフラスコアプリからテンプレートにhtmlデータが挿入されることが予想される場合は、これを使用してください。
OzzyTheGiant 2017年

ノードサーバーでこれを行う方法
Mohammad Kermani

44

Matthias Bynensには、このためのライブラリがあります。https//github.com/mathiasbynens/he

例:

console.log(
    he.decode("J&#246;rg &amp J&#xFC;rgen rocked to &amp; fro ")
);
// Logs "Jörg & Jürgen rocked to & fro"

要素のHTMLコンテンツを設定して、そのテキストコンテンツを読み返すハックよりも、この方法をお勧めします。このようなアプローチは機能しますが、信頼できないユーザー入力で使用すると、一見危険であり、XSSの機会をもたらします。

ライブラリにロードするのに本当に耐えられない場合は、ほぼ同じ質問に対するこの回答でtextarea説明されているハックを使用できます。これは、提案されているさまざまな類似のアプローチとは異なり、私が知っているセキュリティホールはありません。

function decodeEntities(encodedString) {
    var textArea = document.createElement('textarea');
    textArea.innerHTML = encodedString;
    return textArea.value;
}

console.log(decodeEntities('1 &amp; 2')); // '1 & 2'

しかし、リンクされた回答に記載されている、この問題と同様のアプローチに影響を与えるセキュリティの問題に注意してください!このアプローチはハックであり、aの許可されるコンテンツtextarea(または特定のブラウザーのバグ)に対する将来の変更により、ある日突然XSSホールが存在することに依存するコードにつながる可能性があります。


Matthias Bynensのライブラリheは絶対に素晴らしいです!推薦ありがとうございました!
ペドロA

23
var htmlEnDeCode = (function() {
    var charToEntityRegex,
        entityToCharRegex,
        charToEntity,
        entityToChar;

    function resetCharacterEntities() {
        charToEntity = {};
        entityToChar = {};
        // add the default set
        addCharacterEntities({
            '&amp;'     :   '&',
            '&gt;'      :   '>',
            '&lt;'      :   '<',
            '&quot;'    :   '"',
            '&#39;'     :   "'"
        });
    }

    function addCharacterEntities(newEntities) {
        var charKeys = [],
            entityKeys = [],
            key, echar;
        for (key in newEntities) {
            echar = newEntities[key];
            entityToChar[key] = echar;
            charToEntity[echar] = key;
            charKeys.push(echar);
            entityKeys.push(key);
        }
        charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
        entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
    }

    function htmlEncode(value){
        var htmlEncodeReplaceFn = function(match, capture) {
            return charToEntity[capture];
        };

        return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
    }

    function htmlDecode(value) {
        var htmlDecodeReplaceFn = function(match, capture) {
            return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
        };

        return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
    }

    resetCharacterEntities();

    return {
        htmlEncode: htmlEncode,
        htmlDecode: htmlDecode
    };
})();

これはExtJSソースコードからです。


4
-1; これは名前付きエンティティの大部分を処理できません。たとえば、htmlEnDecode.htmlDecode('&euro;')'€'を返しますが、代わりにを返します'&euro;'
Mark Amery 2017


15

Lodash unescape /エスケープ関数を使用できますhttps://lodash.com/docs/4.17.5#unescape

import unescape from 'lodash/unescape';

const str = unescape('fred, barney, &amp; pebbles');

strは 'fred, barney, & pebbles'


1
おそらく「import _unescape from 'lodash / unescape';」を実行する方が良いでしょう。したがって、同じ名前の非推奨のJavaScript関数と競合しません:unescape
Rick Penabella

14

あなたがそれを探している場合のために、私のように-一方、素敵で安全なJQueryメソッドがあります。

https://api.jquery.com/jquery.parsehtml/

あなたはf.exすることができます。これをコンソールに入力します。

var x = "test &amp;";
> undefined
$.parseHTML(x)[0].textContent
> "test &"

したがって、$。parseHTML(x)は配列を返します。テキスト内にHTMLマークアップがある場合、array.lengthは1より大きくなります。


私にとって完璧に機能しました。これはまさに私が探していたものでした、ありがとう。
Jonathan Nielsen

1
上記のx値の場合、<script>alert('hello');</script>クラッシュします。現在のjQueryでは実際にスクリプトを実行しようとはしませんが、結果が[0]出るundefinedのでへの呼び出しtextContentは失敗し、スクリプトはそこで停止します。$('<div />').html(x).text();安全に見える-gist.github.com/jmblog/3222899
Andrew Hodgkinson

@AndrewHodgkinsonええ、でも質問は「デコードしてJavaScriptで&に戻す」でした-したがって、最初にxの内容をテストするか、正しいケースでのみ使用することを確認してください。
cslotty

私はそれがどのように続くのか本当にわかりません。上記のコードはすべてのケースで機能します。そして、修正が必要なxの値をどのくらい正確に「確認」しますか?上記のスクリプト例が「&amp;」を警告した場合 本当に修正が必要だったの?OPの文字列がどこから来たかはわかりません。そのため、悪意のある入力を考慮する必要があります。
Andrew Hodgkinson

@AndrewHodgkinson私はあなたの検討が好きですが、それはここでは問題ではありません。ただし、その質問に自由に回答してください。スクリプトタグf.exを削除できると思います。
cslotty

8

jQueryがエンコードしてデコードします。ただし、divではなく、textareaタグを使用する必要があります。

var str1 = 'One & two & three';
var str2 = "One &amp; two &amp; three";
  
$(document).ready(function() {
   $("#encoded").text(htmlEncode(str1)); 
   $("#decoded").text(htmlDecode(str2));
});

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

function htmlEncode(value) {
  return $('<textarea/>').text(value).html();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<div id="encoded"></div>
<div id="decoded"></div>


2
-1古いjQueryバージョンには(驚くべき)セキュリティホールがあり、そのうちのいくつかはおそらくかなりのユーザーベースを持っているため、これらのバージョンはに渡されるHTML内のスクリプト検出して明示的に評価.html()ます。したがって、textareaここでのセキュリティを確保するには、aを使用するだけでは不十分です。このタスクにはjQueryを使用せず、プレーンなDOM APIで同等のコードを作成することをお勧めします。(はい、jQueryによるその古い動作は気違いでひどいものです。)
Mark Amery 2017

指摘いただきありがとうございます。ただし、質問にはスクリプトインジェクションをチェックする要件は含まれていません。この質問では、WebサーバーによってレンダリングされたHTMLについて具体的に質問します。Webサーバーに保存されたHTMLコンテンツは、保存前にスクリプトインジェクションについて検証される必要があります。
Jason Williams

4

最初に<span id="decodeIt" style="display:none;"></span>体のどこかに作成します

次に、innerHTMLとしてデコードされる文字列をこれに割り当てます。

document.getElementById("decodeIt").innerHTML=stringtodecode

最後に、

stringtodecode=document.getElementById("decodeIt").innerText

全体的なコードは次のとおりです。

var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText

1
-1; これは、信頼できない入力で使用するのは危険なほど危険です。たとえば、のstringtodecodeようなものが含まれているとどうなるかを考えてみましょう<script>alert(1)</script>
Mark Amery 2017

2

一般的なものをキャッチするJavaScriptソリューション:

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

これはhttps://stackoverflow.com/a/4835406/2738039の逆です


map[c] || ''認識されないものを使用すると、次のように表示されませんundefined
Eldelshell

非常に限られた範囲。-1。
Mark Amery 2017

2
+1は、より多くのであるunescapeHtml(str){ var map = {amp: '&', lt: '<', le: '≤', gt: '>', ge: '≥', quot: '"', '#039': "'"} return str.replace(/&([^;]+);/g, (m, c) => map[c]|| '') }
トランクオックホアイは、2015年の新しい

手動カバレッジ。推奨されません。
セルジオA.

2

ワンラインの人のために:

const htmlDecode = innerHTML => Object.assign(document.createElement('textarea'), {innerHTML}).value;

console.log(htmlDecode('Complicated - Dimitri Vegas &amp; Like Mike'));

2

質問は出所を特定していませんxが、可能であれば、悪意のある(または単に予想外の)アプリケーションからの入力から防御することは理にかなっています。たとえばx、の値があるとします&amp; <script>alert('hello');</script>。jQueryでこれを処理する安全で簡単な方法は次のとおりです。

var x    = "&amp; <script>alert('hello');</script>";
var safe = $('<div />').html(x).text();

// => "& alert('hello');"

https://gist.github.com/jmblog/3222899で見つかります。いくつかの代替案よりも短くなくても、少なくとも同じくらい短く、XSSに対する防御提供するので、このソリューションの使用を避ける多くの理由がわかりません。

(私は元々これをコメントとして投稿しましたが、同じスレッドでの後続のコメントが私にそうするように要求したので、それを回答として追加しています)。


1

JSON配列から&を削除するためにあらゆることを試みました。上記の例はありませんが、https://stackoverflow.com/users/2030321/chrisは優れたソリューションを提供してくれて、問題を解決することができました。

var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText

JSONデータを配列にプルするモーダルウィンドウに挿入する方法がわからなかったので、使用しませんでしたが、例に基づいてこれを試しましたが、うまくいきました。

var modal = document.getElementById('demodal');
$('#ampersandcontent').text(replaceAll(data[0],"&amp;", "&"));

シンプルで機能するので気に入っていますが、なぜ広く使われていないのかはわかりません。hi&lowを検索して、簡単な解決策を見つけました。私は構文の理解を求め続けていますが、これを使用するリスクがある場合は。まだ何も見つかりません。


最初の提案は少しトリッキーですが、多くの努力をしなくてもうまく機能します。一方、2番目の方法では、ブルートフォースのみを使用して文字をデコードします。これは、完全なデコード機能を実現するために多くの労力と時間がかかることを意味します。OPの問題を解決するために誰もその方法を使用していないのはそのためです。
セルジオA.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.