Dekodieren & amp; zurück zu & in JavaScript

229

Ich habe Saiten wie

var str = 'One & two & three';

vom Webserver in HTML gerendert. Ich muss diese Saiten in verwandeln

'One & two & three'

Derzeit mache ich das (mit Hilfe von jQuery):

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

Ich habe jedoch ein beunruhigendes Gefühl, dass ich es falsch mache. Ich habe versucht

unescape("&")

aber es scheint nicht zu funktionieren, decodeURI / decodeURIComponent auch nicht.

Gibt es andere, einheimischere und elegantere Möglichkeiten?

Kunst
quelle
Die riesige Funktion in diesem Artikel scheint gut zu funktionieren: blogs.msdn.com/b/aoakley/archive/2003/11/12/49645.aspx Ich denke nicht, dass dies die cleverste Lösung ist, aber funktioniert.
Matias
1
Da Zeichenfolgen, die HTML-Entitäten enthalten, etwas anderes sind als escaped- oder URI-codierte Zeichenfolgen , funktionieren diese Funktionen nicht.
Marcel Korpel
1
@Matias Beachten Sie, dass HTML neue benannte Entitäten hinzugefügt wurden (z. B. über die HTML 5-Spezifikation), seit diese Funktion 2003 erstellt wurde - zum Beispiel wird sie nicht erkannt 𝕫. Dies ist ein Problem mit einer sich entwickelnden Spezifikation. Als solches sollten Sie ein Werkzeug auswählen, das tatsächlich gewartet wird, um es zu lösen.
Mark Amery
1
@ MarkAmery ja, ich stimme vollkommen zu! Es ist eine schöne Erfahrung, nach ein paar Jahren auf diese Fragen zurückzukommen, danke!
Matias

Antworten:

103

Eine modernere Option zum Interpretieren von HTML (Text und andere) aus JavaScript ist die HTML-Unterstützung in der DOMParserAPI ( siehe hier in MDN ). Auf diese Weise können Sie den nativen HTML-Parser des Browsers verwenden, um eine Zeichenfolge in ein HTML-Dokument zu konvertieren. Es wird seit Ende 2014 in neuen Versionen aller gängigen Browser unterstützt.

Wenn wir nur Textinhalte dekodieren möchten, können wir ihn als einzigen Inhalt in einen Dokumentkörper einfügen, das Dokument analysieren und den Inhalt herausziehen .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);

Wir können im Entwurf der Spezifikation sehen,DOMParser dass JavaScript für das analysierte Dokument nicht aktiviert ist, sodass wir diese Textkonvertierung ohne Sicherheitsbedenken durchführen können.

Die parseFromString(str, type)Methode muss diese Schritte je nach Typ ausführen :

  • "text/html"

    Analysieren Sie str mit einem HTML parserund geben Sie das neu erstellte zurückDocument .

    Das Skriptflag muss auf "deaktiviert" gesetzt sein.

    HINWEIS

    scriptElemente werden als nicht ausführbar markiert und der Inhalt von wird noscriptals Markup analysiert.

Es würde den Rahmen dieser Frage sprengen , aber bitte beachten Sie, dass, wenn Sie die analysierten DOM-Knoten selbst (nicht nur ihren Textinhalt) nehmen und sie in das Live-Dokument-DOM verschieben, es möglich ist, dass ihre Skripterstellung wieder aktiviert wird, und dies könnte der Fall sein Sicherheitsbedenken sein. Ich habe es nicht recherchiert, also seien Sie bitte vorsichtig.

Jeremy Banks
quelle
4
eine Alternative für NodeJs?
coderInrRain
284

Müssen Sie alle codierten HTML-Entitäten oder nur sich &amp;selbst dekodieren ?

Wenn Sie nur damit umgehen müssen, &amp;können Sie dies tun:

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

Wenn Sie alle HTML-Entitäten dekodieren müssen, können Sie dies ohne jQuery tun:

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

Bitte beachten Sie die folgenden Kommentare von Mark, die Sicherheitslücken in einer früheren Version dieser Antwort hervorheben, und empfehlen, potenzielle XSS-Schwachstellen zu verwenden, textareaanstatt sie divzu mindern. Diese Sicherheitsanfälligkeiten bestehen unabhängig davon, ob Sie jQuery oder einfaches JavaScript verwenden.

LukeH
quelle
16
In acht nehmen! Dies ist möglicherweise unsicher. In encoded='<img src="bla" onerror="alert(1)">'diesem Fall wird im obigen Snippet eine Warnung angezeigt. Dies bedeutet, wenn Ihr codierter Text von Benutzereingaben stammt, kann das Decodieren mit diesem Snippet eine XSS-Sicherheitsanfälligkeit darstellen.
Mark Amery
@ MarkAmery Ich bin kein Sicherheitsexperte, aber es sieht so aus, als ob Sie die Div sofort nullnach dem Abrufen
Mottie
4
@Mottie notiere dir, in welchem ​​Browser das für dich funktioniert hat, aber das alert(1)wird für mich unter Chrome unter OS X immer noch ausgelöst. Wenn du eine sichere Variante dieses Hacks willst, versuche es mit einemtextarea .
Mark Amery
+1 für die einfache Regexp-Ersatzalternative für nur eine Art von HTML-Entität. Verwenden Sie diese Option, wenn Sie erwarten, dass HTML-Daten beispielsweise von einer Python-Flask-App in eine Vorlage interpoliert werden.
OzzyTheGiant
Wie geht das auf dem Node Server?
Mohammad Kermani
44

Matthias Bynens hat dafür eine Bibliothek: https://github.com/mathiasbynens/he

Beispiel:

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

Ich schlage vor, es Hacks vorzuziehen, bei denen der HTML-Inhalt eines Elements festgelegt und dann dessen Textinhalt zurückgelesen wird. Solche Ansätze können funktionieren, sind jedoch täuschend gefährlich und bieten XSS-Möglichkeiten, wenn sie für nicht vertrauenswürdige Benutzereingaben verwendet werden.

Wenn Sie es wirklich nicht ertragen können, in eine Bibliothek zu laden, können Sie den textareain dieser Antwort beschriebenen Hack auf eine nahezu doppelte Frage anwenden, die im Gegensatz zu verschiedenen ähnlichen Ansätzen, die vorgeschlagen wurden, keine mir bekannten Sicherheitslücken aufweist:

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

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

Beachten Sie jedoch die Sicherheitsprobleme, die ähnliche Ansätze betreffen, die ich in der verknüpften Antwort aufführe! Dieser Ansatz ist ein Hack, und zukünftige Änderungen am zulässigen Inhalt eines textarea(oder an Fehlern in bestimmten Browsern) können dazu führen, dass Code eines Tages plötzlich ein XSS-Loch aufweist.

Mark Amery
quelle
Die Bibliothek von Matthias Bynens heist absolut großartig! Vielen Dank für die Empfehlung!
Pedro 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
    };
})();

Dies ist aus ExtJS-Quellcode.

WaiKit Kung
quelle
4
-1; Dies kann die überwiegende Mehrheit der genannten Entitäten nicht bewältigen. Zum Beispiel htmlEnDecode.htmlDecode('&euro;')sollte zurückkehren '€', aber stattdessen zurückgeben '&euro;'.
Mark Amery
17

element.innerText macht auch den Trick.

avg_joe
quelle
15

Sie können die Lodash Unescape / Escape-Funktion https://lodash.com/docs/4.17.5#unescape verwenden

import unescape from 'lodash/unescape';

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

str wird werden 'fred, barney, & pebbles'

Ich bin ich
quelle
1
wahrscheinlich besser "importiere _unescape von 'lodash / unescape';" es widerspricht also nicht der veralteten gleichnamigen Javascript-Funktion: unescape
Rick Penabella
14

Für den Fall, dass Sie danach suchen, wie ich - mittlerweile gibt es eine nette und sichere JQuery-Methode.

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

Sie können f.ex. Geben Sie dies in Ihre Konsole ein:

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

$ .ParseHTML (x) gibt also ein Array zurück. Wenn Sie HTML-Markup in Ihrem Text haben, ist die array.length größer als 1.

cslotty
quelle
Hat perfekt für mich funktioniert, genau das habe ich gesucht, danke.
Jonathan Nielsen
1
Wenn xein Wert <script>alert('hello');</script>der oben genannten hat, stürzt ab. In der aktuellen jQuery wird nicht versucht, das Skript auszuführen, sondern es [0]wird nachgegeben, undefinedsodass der Aufruf von textContentfehlschlägt und Ihr Skript dort stoppt. $('<div />').html(x).text();sieht sicherer aus - via gist.github.com/jmblog/3222899
Andrew Hodgkinson
@AndrewHodgkinson Ja, aber die Frage war "Dekodieren & zurück zu & in JavaScript" - also würden Sie zuerst den Inhalt von x testen oder sicherstellen, dass Sie ihn nur in den richtigen Fällen verwenden.
Cslotty
Ich sehe nicht wirklich, wie das folgt. Der obige Code funktioniert in allen Fällen. Und wie genau würden Sie den Wert von x "sicherstellen", der korrigiert werden muss? Und was ist, wenn das obige Skriptbeispiel "& amp;" damit es wirklich korrigiert werden musste? Wir haben keine Ahnung, woher die Zeichenfolgen des OP stammen, daher müssen böswillige Eingaben berücksichtigt werden.
Andrew Hodgkinson
@ AndrewHodgkinson Ich mag Ihre Überlegung, aber das ist hier nicht die Frage. Fühlen Sie sich jedoch frei, diese Frage zu beantworten. Ich denke, Sie könnten Skript-Tags entfernen, z.
Cslotty
8

jQuery wird für Sie codieren und decodieren. Sie müssen jedoch ein Textarea-Tag verwenden, kein div.

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>

Jason Williams
quelle
2
-1, da es hier eine (überraschende) Sicherheitslücke für alte jQuery-Versionen gibt, von denen einige wahrscheinlich noch eine bedeutende Benutzerbasis haben - diese Versionen erkennen und bewerten Skripte in dem an übergebenen HTML explizit.html() . Daher textareareicht es nicht aus, a zu verwenden, um hier die Sicherheit zu gewährleisten. Ich empfehle , jQuery für diese Aufgabe nicht zu verwenden und äquivalenten Code mit der einfachen DOM-API zu schreiben . (Ja, dieses alte Verhalten von jQuery ist verrückt und schrecklich.)
Mark Amery
Vielen Dank für den Hinweis. Die Frage enthält jedoch keine Anforderung zur Überprüfung der Skriptinjektion. In der Frage wird speziell nach HTML gefragt, das vom Webserver gerendert wird. Auf einem Webserver gespeicherte HTML-Inhalte sollten wahrscheinlich vor dem Speichern auf Skriptinjektion überprüft werden.
Jason Williams
4

Erstellen Sie zuerst eine <span id="decodeIt" style="display:none;"></span> irgendwo im Körper

Weisen Sie als Nächstes die Zeichenfolge zu, die als innerHTML dekodiert werden soll:

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

Schließlich,

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

Hier ist der Gesamtcode:

var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText
Infoglaze.com
quelle
1
-1; Dies ist gefährlich unsicher bei nicht vertrauenswürdigen Eingaben. Überlegen Sie zum Beispiel, was passiert, wenn es so stringtodecodeetwas enthält <script>alert(1)</script>.
Mark Amery
2

eine Javascript-Lösung, die die gängigen fängt:

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

Dies ist die Umkehrung von https://stackoverflow.com/a/4835406/2738039

Peter Brandt
quelle
Wenn Sie map[c] || ''nicht erkannte verwenden, werden diese nicht alsundefined
Eldelshell
Sehr begrenzte Abdeckung; -1.
Mark Amery
2
+1, mehr istunescapeHtml(str){ var map = {amp: '&', lt: '<', le: '≤', gt: '>', ge: '≥', quot: '"', '#039': "'"} return str.replace(/&([^;]+);/g, (m, c) => map[c]|| '') }
Trần Quốc Hoài neu 2015
Manuelle Abdeckung. Nicht empfohlen.
Sergio A.
2

Für einzeilige Leute:

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

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

Die Frage gibt nicht den Ursprung von an, xaber es ist sinnvoll, wenn möglich gegen böswillige (oder nur unerwartete) Eingaben aus unserer eigenen Anwendung zu verteidigen. Angenommen, xhat einen Wert von &amp; <script>alert('hello');</script>. Eine sichere und einfache Möglichkeit, dies in jQuery zu handhaben, ist:

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

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

Gefunden über https://gist.github.com/jmblog/3222899 . Ich sehe nicht viele Gründe, diese Lösung zu vermeiden, da sie mindestens so kurz ist, wenn nicht sogar kürzer als einige Alternativen und Schutz gegen XSS bietet.

(Ich habe dies ursprünglich als Kommentar gepostet, füge es aber als Antwort hinzu, da ein nachfolgender Kommentar im selben Thread mich dazu aufgefordert hat.)

Andrew Hodgkinson
quelle
1

Ich habe alles versucht, um & aus einem JSON-Array zu entfernen. Keines der oben genannten Beispiele, aber https://stackoverflow.com/users/2030321/chris ergab eine großartige Lösung, die mich dazu brachte, mein Problem zu beheben.

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

Ich habe es nicht verwendet, weil ich nicht verstanden habe, wie man es in ein modales Fenster einfügt, das JSON-Daten in ein Array zieht, aber ich habe dies anhand des Beispiels versucht, und es hat funktioniert:

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

Ich mag es, weil es einfach war und funktioniert, aber nicht sicher, warum es nicht weit verbreitet ist. Hi & Low gesucht, um eine einfache Lösung zu finden. Ich bemühe mich weiterhin um ein Verständnis der Syntax und ob ein Risiko besteht, diese zu verwenden. Habe noch nichts gefunden.

Digexart
quelle
Ihr erster Vorschlag ist nur ein bisschen knifflig, aber er funktioniert ohne großen Aufwand. Der zweite hingegen verwendet nur rohe Gewalt, um Zeichen zu dekodieren. Dies bedeutet, dass es eine Menge Aufwand und Zeit in Anspruch nehmen kann, um eine vollständige Decodierungsfunktion zu erreichen. Deshalb nutzt niemand diesen Weg, um das Problem von OP zu lösen.
Sergio A.