Jak dekodować encje HTML za pomocą jQuery?

334

Jak używać jQuery do dekodowania encji HTML w ciągu?

EddyR
źródło
Przedwczesny wybór technologii (jQuery) zachęca do rozwiązywania problemów związanych z bezpieczeństwem. To może być lepiej zamknięty jako duplikat stackoverflow.com/questions/1912501/... .
Wladimir Palant

Odpowiedzi:

437

Uwaga bezpieczeństwa: użycie tej odpowiedzi (zachowanej w oryginalnej formie poniżej) może wprowadzić w aplikacji lukę w zabezpieczeniach XSS . Nie powinieneś używać tej odpowiedzi. Przeczytaj odpowiedź Lucasa na wyjaśnienie luk w tej odpowiedzi i zamiast tego użyj podejścia z tej odpowiedzi lub odpowiedzi Marka Ameryka .

Właściwie spróbuj

var decoded = $("<div/>").html(encodedStr).text();
Tomek
źródło
175
Czy nie to zrobić z wejściem niezaufane. Wiele przeglądarek ładuje obrazy i zdarzenia związane z pożarem, nawet jeśli węzeł nie jest przyłączony do DOM. Spróbuj uruchomić $("<div/>").html('<img src="http://www.google.com/images/logos/ps_logo2.png" onload=alert(1337)>'). W przeglądarce Firefox lub Safari uruchamia alert.
Mike Samuel
@Mike, więc co zamiast tego polecasz? twoja odpowiedź na .replace () nie jest dobra, jeśli nie wiesz, co zastępujesz ...
ekkis
7
@ekkis, musisz usunąć tagi przed próbą odkodowania encji. str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/g, "")lub coś podobnego.
Mike Samuel
2
Lepsza implementacja (moim zdaniem), która usuwa większość tagów HTML (dzięki uprzejmości Mike'a) z danych wejściowych, jest w mojej odpowiedzi na podobne pytanie . Nie ma też narzutów związanych z jQuery, więc jest całkiem odpowiedni dla innych środowisk.
Robert K
6
@MichaelStum twoja edycja tutaj unieważniła zarówno komentarz Mike'a Samuela, jak i następną najwyżej ocenioną odpowiedź, i zrobiła to bez naprawiania podatności XSS dla wszystkich wersji jQuery (jak wyjaśniono w odpowiedzi poniżej). Dodanie ostrzeżenia dotyczącego bezpieczeństwa do tej odpowiedzi byłoby rozsądne (i zamierzam to zrobić); sprawianie, by inne dyskusje na tej stronie były bezsensowne, podczas gdy nie udało się naprawić dziury w zabezpieczeniach, zdecydowanie nie jest!
Mark Amery
211

Bez jQuery:

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

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

Działa to podobnie do zaakceptowanej odpowiedzi , ale można go bezpiecznie stosować przy niezaufanym wkładzie użytkownika.


Problemy bezpieczeństwa w podobnych podejściach

Jak zauważył Mike Samuela , robi to z <div>zamiast <textarea>z niezaufanych danych wejściowych użytkownika jest luka XSS, nawet jeśli <div>nigdy nie zostanie dodany do DOM:

function decodeEntities(encodedString) {
  var div = document.createElement('div');
  div.innerHTML = encodedString;
  return div.textContent;
}

// Shows an alert
decodeEntities('<img src="nonexistent_image" onerror="alert(1337)">')

Jednak ten atak nie jest możliwy przeciwko, <textarea>ponieważ nie ma elementów HTML, które są dozwolone w treści <textarea>. W związku z tym wszelkie tagi HTML wciąż obecne w ciągu „zakodowanego” zostaną automatycznie zakodowane przez przeglądarkę.

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

// Safe, and returns the correct answer
console.log(decodeEntities('<img src="nonexistent_image" onerror="alert(1337)">'))

Ostrzeżenie : Robienie tego przy użyciu jQuery .html()i .val()metod zamiast korzystania .innerHTMLi .valuejest również niepewne * dla niektórych wersji jQuery, nawet gdy używasztextarea . Wynika to z faktu, że starsze wersje jQuery celowo i jawnie oceniały skrypty zawarte w przekazywanym ciągu .html(). Dlatego taki kod pokazuje alert w jQuery 1.8:

//<!-- CDATA
// Shows alert
$("<textarea>")
.html("<script>alert(1337);</script>")
.text();

//-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>

* Podziękowania dla Eru Penkman za wyłapanie tej podatności.

lucascaro
źródło
6
Dobrym pomysłem może być zniszczenie obszaru tekstowego po wyodrębnieniu jego wartości:decodedString = textArea.value; textArea.remove(); return decodedString;
Werner
2
Lub tylko wtedy, gdy wersja javascript faktycznie obsługuje remove ():if ('remove' in Element.prototype) textArea.remove();
Werner
6
@Werner Zaraz po wyjściu z funkcji nie będzie już żadnych zmiennych zawierających odwołanie, więc zostanie on automatycznie usunięty przez moduł odśmiecający .
user2428118,
Używam tego w połączeniu z .NET od kodu za kliknięciem przycisku, a z jakiegoś powodu zaakceptowana odpowiedź spowodowała odesłanie. Ta odpowiedź nie, więc jest to dla mnie najlepsza odpowiedź. Dzięki!
Snailer
@Snailer $("<div />").html(string).text() wykona dowolny kod JavaScript w podanym ciągu , co, jak podejrzewam, jest przyczyną problemu. Przyjęta odpowiedź powinna zostać zaktualizowana do tej.
łucznik
80

Jak powiedział Mike Samuel, nie używaj jQuery.html (). Text () do dekodowania jednostek HTML, ponieważ jest to niebezpieczne.

Zamiast tego użyj renderera szablonów, takiego jak Mustache.js lub decodeEntities z komentarza @ VyvIT.

Underscore.js biblioteka narzędzie pas wyposażony escapei unescapemetod, ale nie są one bezpieczne dla danych wejściowych użytkownika:

_.escape (ciąg)

_.unescape (ciąg)

Alan Hamlett
źródło
2
To zasługuje na więcej pozytywnych opinii! Zdecydowanie moje preferowane rozwiązanie. Do unescapetej pory włączali się do dokumentów, btw.
zabójcza gitara
5
_.unescape("&#39;")powoduje tylko „& # 39;” zamiast pojedynczego cytatu. Czy brakuje mi czegoś lub podkreślenie nie powoduje ucieczki do kodów encji HTML, jak pokazano na: w3schools.com/tags/ref_entities.asp
Jason Axelson
6
Błąd w github został zamknięty jako „Won't fix”; oznacza to, że to rozwiązanie nie działa i nie będzie działać.
Igor Chubin
3
Mówisz, że podkreślenia escapei unescapemetody… nie są bezpieczne dla danych wprowadzanych przez użytkownika” . Co przez to rozumiesz? Dla mnie to brzmi jak nonsens, ale może coś mi brakuje - czy możesz to wyjaśnić?
Mark Amery
2
@VyvIT Próbowałem _.unescape("&lt;img src=fake onerror=alert('boo!')&gt;")(w Chrome / FF / IE). Ale to nie pokazało żadnego ostrzeżenia. Wypróbowałem to w konsoli, a także umieściłem w moim pliku JS. Ten sam wynik.
Vivek Athalye
28

Myślę, że mylisz metody tekstowe i HTML. Spójrz na ten przykład, jeśli użyjesz wewnętrznego HTML elementu jako tekstu, otrzymasz dekodowane tagi HTML (drugi przycisk). Ale jeśli użyjesz ich jako HTML, otrzymasz widok w formacie HTML (pierwszy przycisk).

<div id="myDiv">
    here is a <b>HTML</b> content.
</div>
<br />
<input value="Write as HTML" type="button" onclick="javascript:$('#resultDiv').html($('#myDiv').html());" />
&nbsp;&nbsp;
<input value="Write as Text" type="button" onclick="javascript:$('#resultDiv').text($('#myDiv').html());" />
<br /><br />
<div id="resultDiv">
    Results here !
</div>

Pierwszy przycisk pisze: oto treść HTML .

Drugi przycisk pisze: oto treść <B> HTML </B>.

Nawiasem mówiąc, możesz zobaczyć wtyczkę, którą znalazłem we wtyczce jQuery - Dekodowanie i kodowanie HTML, które koduje i dekoduje ciągi HTML.

Canavar
źródło
26

Pytanie jest ograniczone przez „with jQuery”, ale może pomóc niektórym wiedzieć, że kod jQuery podany w najlepszej tutaj odpowiedzi ma następujące cechy ... działa to z lub bez jQuery:

function decodeEntities(input) {
  var y = document.createElement('textarea');
  y.innerHTML = input;
  return y.value;
}
Rondo
źródło
20

Możesz skorzystać z biblioteki on , dostępnej na stronie https://github.com/mathiasbynens/he

Przykład:

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

I zakwestionował autor biblioteki na pytanie, czy istnieje jakikolwiek powód, aby korzystać z tej biblioteki w kodzie stronie klienta na rzecz <textarea>hack, przewidzianej w innych odpowiedzi tutaj i gdzie indziej. Podał kilka możliwych uzasadnień:

  • Jeśli używasz strony node.js po stronie serwera, użycie biblioteki do kodowania / dekodowania HTML daje jedno rozwiązanie, które działa zarówno po stronie klienta, jak i po stronie serwera.

  • Algorytmy dekodowania encji niektórych przeglądarek zawierają błędy lub brakuje obsługi niektórych nazwanych odwołań do znaków . Na przykład Internet Explorer zarówno &nbsp;poprawnie dekoduje, jak i renderuje spacje nierozdzielające ( ), ale zgłasza je jako spacje zwykłe zamiast nierozdzielających za pomocą innerTextwłaściwości elementu DOM , przerywając <textarea>włamanie (choć tylko w niewielkim stopniu). Ponadto IE 8 i 9 po prostu nie obsługują żadnego z nowych nazwanych odniesień do znaków dodanych w HTML 5. Autor jego również organizuje test obsługi nazwanych odniesień do znaków na stronie http://mathias.html5.org/tests/html / names-character-reference / . W IE 8 zgłasza ponad tysiąc błędów.

    Jeśli chcesz być izolowany od błędów przeglądarki związanych z dekodowaniem encji i / lub być w stanie obsłużyć pełny zakres odniesień do nazwanych postaci, nie możesz uciec od <textarea>włamania; potrzebujesz biblioteki takiej jak on .

  • Po prostu dobrze się czuje, że robienie rzeczy w ten sposób jest mniej hackerskie.

Mark Amery
źródło
4
+1 jQuery nie jest rozwiązaniem wszystkiego. Użyj odpowiedniego narzędzia do pracy.
Mathias Bynens
To najlepszy sposób na dekodowanie encji HTML. Wszystkie pozostałe odpowiedzi (na to i podobne pytania) albo używają innerHTML (utwórz nowy element HTML, przetwarzaj kod HTML, a następnie uzyskaj innerHTML tego elementu, może to być podatne na ataki XSS, jeśli nie jesteś BARDZO ostrożny, zobacz więcej ), lub proponuję za pomocą underscore.js unescape lub Lodash unescape metod, które są zarówno niekompletne (działa tylko przez kilka podmiotów HTML). Biblioteka on jest najbardziej kompletną i bezpieczną opcją!
łączeniu
18

kodować:

$("<textarea/>").html('<a>').html();      // return '&lt;a&gt'

rozszyfrować:

$("<textarea/>").html('&lt;a&gt').val()   // return '<a>'
użytkownik4064396
źródło
3
odpowiedź już działa i jest prawie identyczna z tym. Nie potrzebujemy duplikatów odpowiedzi
markasoftware
4
To jest poprawna odpowiedź. Odpowiedź Toma wykorzystuje element DIV, co czyni tę odpowiedź podatną na XSS.
Francisco Hodge,
2
To najlepsza odpowiedź na jasność.
Dan Randolph
4

Posługiwać się

myString = myString.replace( /\&amp;/g, '&' );

Najłatwiej jest to zrobić po stronie serwera, ponieważ najwyraźniej JavaScript nie ma natywnej biblioteki do obsługi encji, ani nie znalazłem żadnej w górnej części wyników wyszukiwania dla różnych struktur rozszerzających JavaScript.

Wyszukaj „JavaScript HTML encities”, a może znajdziesz kilka bibliotek tylko do tego celu, ale prawdopodobnie wszystkie one zostaną zbudowane wokół powyższej logiki - zamień encję po encji.

Peter Mortensen
źródło
0

Po prostu musiałem mieć charater encji HTML (⇓) jako wartość dla przycisku HTML. Kod HTML wygląda dobrze od samego początku w przeglądarce:

<input type="button" value="Embed & Share  &dArr;" id="share_button" />

Teraz dodałem przełącznik, który powinien również wyświetlać znak. To jest moje rozwiązanie

$("#share_button").toggle(
    function(){
        $("#share").slideDown();
        $(this).attr("value", "Embed & Share " + $("<div>").html("&uArr;").text());
    }

Wyświetli się ponownie the w przycisku. Mam nadzieję, że to może komuś pomóc.

philipp
źródło
Prostsze byłoby użycie sekwencji ucieczki unicode (tj. "Embed & Share \u21d1") Lub jeszcze lepiej, tylko "Embed & Share ⇑"jeśli jesteś w stanie obsłużyć swój skrypt w UTF-8 (lub UTF-16 lub innym kodowaniu, które obsługuje znak ⇑). Wykorzystanie elementu DOM do parsowania encji HTML tylko w celu upieczenia dowolnego znaku Unicode w ciągu JavaScript jest sprytnym i kreatywnym podejściem, które sprawiłoby, że Rube Goldberg byłby dumny, ale nie jest dobrą praktyką; sekwencje specjalne unicode są w języku specjalnie do obsługi tego przypadku użycia.
Mark Amery
0

Musisz stworzyć niestandardową funkcję dla encji HTML:

function htmlEntities(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g,'&gt;').replace(/"/g, '&quot;');
}
Ali
źródło
Nie mam pojęcia, pomogło mi więc +1 l-)
Szymon Toda
być może został odrzucony, ponieważ obsługuje tylko niektóre podmioty.
Jasen
Pierwotne pytanie brzmiało: jak dekodować byty - robi to odwrotność tego, co jest pożądane; to koduje bardzo ograniczony zestaw znaków do podmiotów. Jak mówi opis głosowania w dół, „Ta odpowiedź nie jest przydatna”. Dziwi mnie, że po 4 latach wciąż ma dodatni wynik netto.
Stephen P
0

Załóżmy, że masz poniżej String.

Nasze kabiny Deluxe są ciepłe, przytulne i wyposażone; wygodny

var str = $("p").text(); // get the text from <p> tag
$('p').html(str).text();  // Now,decode html entities in your variable i.e 

str i przypisz z powrotem do

etykietka.

Otóż ​​to.

Anirudh Sood
źródło
0

W przypadku użytkowników ExtJS, jeśli masz już zakodowany ciąg, na przykład gdy zwróconą wartością funkcji bibliotecznej jest zawartość innerHTML, rozważ tę funkcję ExtJS:

Ext.util.Format.htmlDecode(innerHtmlContent)
Ilan
źródło
Będzie to działać tylko w przypadku 5 jednostek HTML. Możesz to zobaczyć w dokumentacji i kodzie źródłowym .
łączeniu
0

Rozszerz klasę String:

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

i użyj jako metody:

"&lt;img src='myimage.jpg'&gt;".decode()
Sergio Belevskij
źródło
0

Spróbuj tego :

var htmlEntities = "&lt;script&gt;alert('hello');&lt;/script&gt;";
var htmlDecode =$.parseHTML(htmlEntities)[0]['wholeText'];
console.log(htmlDecode);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

parseHTML jest funkcją w bibliotece Jquery i zwraca tablicę, która zawiera pewne szczegóły dotyczące danego ciągu.

w niektórych przypadkach łańcuch jest duży, więc funkcja podzieli zawartość na wiele indeksów.

i aby uzyskać wszystkie dane indeksów, należy przejść do dowolnego indeksu, a następnie uzyskać dostęp do indeksu o nazwie „wholeText”.

Wybrałem indeks 0, ponieważ będzie on działał we wszystkich przypadkach (mały ciąg lub duży ciąg).

Fawaz Al Romy
źródło
Ten fragment kodu może być rozwiązaniem, ale wyjaśnienie naprawdę pomaga poprawić jakość posta. Pamiętaj, że w przyszłości odpowiadasz na pytanie dla czytelników, a ci ludzie mogą nie znać przyczyn Twojej sugestii kodu.
Johan
Wyjaśnienie zostało dodane ... Dziękuję :)
Fawaz Al Romy
-1

Pozostaje jeszcze jeden problem: Ciąg znaków ucieczki nie wygląda na czytelny po przypisaniu do wartości wejściowej

var string = _.escape("<img src=fake onerror=alert('boo!')>");
$('input').val(string);

Przykład: https://jsfiddle.net/kjpdwmqa/3/

Lauris Kuznecovs
źródło
To nie jest odpowiedź na pytanie. OP prosi o zdekodowanie (unescape) encji HTML, ale w tej odpowiedzi używasz escapemetody Underscore.js. Nie ma też wyjaśnienia, w jaki sposób próbka kodu powinna rozwiązać problem OP.
łączeniu
-1

Alternatywnie jest też biblioteka dla niego ..

tutaj https://cdnjs.com/libraries/he

npm install he                 //using node.js

<script src="js/he.js"></script>  //or from your javascript directory

Użycie jest następujące ...

//to encode text 
he.encode('© Ande & Nonso® Company LImited 2018');  

//to decode the 
he.decode('&copy; Ande &amp; Nonso&reg; Company Limited 2018');

Twoje zdrowie.

Ande Caleb
źródło
Istnieje już odpowiedź na temat biblioteki, która jest kompletna, z prostym przykładem kodu i dobrym wyjaśnieniem, dlaczego i kiedy należy korzystać z biblioteki .
łączeniu
-3

Aby zdekodować encje HTML za pomocą jQuery, wystarczy użyć tej funkcji:

function html_entity_decode(txt){
    var randomID = Math.floor((Math.random()*100000)+1);
    $('body').append('<div id="random'+randomID+'"></div>');
    $('#random'+randomID).html(txt);
    var entity_decoded = $('#random'+randomID).html();
    $('#random'+randomID).remove();
    return entity_decoded;
}

Jak używać:

JavaScript:

var txtEncoded = "&aacute; &eacute; &iacute; &oacute; &uacute;";
$('#some-id').val(html_entity_decode(txtEncoded));

HTML:

<input id="some-id" type="text" />
Fred
źródło
-3

Najprostszym sposobem jest ustawienie selektora klasy na elementy, a następnie użycie następującego kodu:

$(function(){
    $('.classSelector').each(function(a, b){
        $(b).html($(b).text());
    });
});

Nic więcej nie jest potrzebne!

Miałem ten problem i znalazłem to jasne rozwiązanie, które działa dobrze.

Hamidreza
źródło
To nie jest odpowiedź na pytanie OP. OP prosi o zdekodowanie encji HTML w STRING, NIE tylko to nie rozwiązuje problemu OP, ale także zastępuje ocalałe encje HTML w elemencie HTML nieosłoniętymi, których nie należy robić.
łączeniu
-3

Myślę, że jest to dokładne przeciwieństwo wybranego rozwiązania.

var decoded = $("<div/>").text(encodedStr).html();
Pedro
źródło