¿Cómo decodificar entidades HTML usando jQuery?

Respuestas:

437

Nota de seguridad: el uso de esta respuesta (preservada en su forma original a continuación) puede introducir una vulnerabilidad XSS en su aplicación. No deberías usar esta respuesta. Lea la respuesta de lucascaro para obtener una explicación de las vulnerabilidades en esta respuesta, y utilice el enfoque de esa respuesta o de la respuesta de Mark Amery .

En realidad, intenta

var decoded = $("<div/>").html(encodedStr).text();
tom
fuente
175
No no hacer esto con la entrada no es de confianza. Muchos navegadores cargan imágenes y activan eventos relacionados, incluso si el nodo no está conectado al DOM. Intenta correr $("<div/>").html('<img src="http://www.google.com/images/logos/ps_logo2.png" onload=alert(1337)>'). En Firefox o Safari, activa la alerta.
Mike Samuel
@ Mike, ¿qué recomiendas en su lugar? su respuesta de .replace () no es bueno si usted no sabe lo que está reemplazando ...
ekkis
77
@ekkis, debe quitar las etiquetas antes de intentar decodificar entidades. str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/g, "")o algo similar.
Mike Samuel
2
Una mejor implementación (en mi opinión) que elimina la mayoría de las etiquetas HTML (cortesía de Mike) de la entrada está en mi respuesta a una pregunta similar . Tampoco tiene la sobrecarga de jQuery, por lo que es bastante adecuado para otros entornos.
Robert K
66
@MichaelStum su edición aquí invalidó tanto el comentario de Mike Samuel como la siguiente respuesta más votada, y lo hizo sin corregir la vulnerabilidad XSS para todas las versiones de jQuery (como se explica en la respuesta a continuación). Agregar una advertencia de seguridad a esta respuesta sería razonable (y lo haré); ¡hacer que otra discusión en esta página no tenga sentido mientras que en realidad no soluciona el agujero de seguridad definitivamente no lo es!
Mark Amery
211

Sin ningún jQuery:

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

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

Esto funciona de manera similar a la respuesta aceptada , pero es seguro de usar con la entrada del usuario no confiable.


Problemas de seguridad en enfoques similares

Como señaló Mike Samuel , hacer esto con una entrada de usuario en <div>lugar de una <textarea>sin confianza es una vulnerabilidad XSS, incluso si <div>nunca se agrega al 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)">')

Sin embargo, este ataque no es posible contra a <textarea>porque no hay elementos HTML que tengan contenido permitido de a <textarea>. En consecuencia, cualquier etiqueta HTML que todavía esté presente en la cadena 'codificada' será automáticamente codificada por el navegador.

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)">'))

Advertencia : Hacer esto usando jQuery .html()y .val()métodos en lugar de usar .innerHTMLy .valuetambién es inseguro * para algunas versiones de jQuery, incluso cuando se usa atextarea . Esto se debe a que las versiones anteriores de jQuery evaluarían deliberada y explícitamente los scripts contenidos en la cadena que se pasa .html(). Por lo tanto, un código como este muestra una alerta en 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>

* Gracias a Eru Penkman por detectar esta vulnerabilidad.

lucascaro
fuente
66
Podría ser una buena idea destruir el área de texto después de extraer su valor:decodedString = textArea.value; textArea.remove(); return decodedString;
Werner
2
O solo si la versión de javascript realmente admite remove ():if ('remove' in Element.prototype) textArea.remove();
Werner
66
@Werner Tan pronto como la función haya salido, no habrá más variables que contengan una referencia para que el recolector de basura la elimine automáticamente .
user2428118
Estoy usando esto en combinación con .NET desde el código subyacente de un clic de botón, y por alguna razón la respuesta aceptada causó una devolución de datos. Esta respuesta no fue así, así que esta es la mejor respuesta para mí. ¡Gracias!
Snailer
@Snailer $("<div />").html(string).text() ejecutará cualquier javascript en la cadena provista , lo que sospecho es lo que estaba causando su problema. La respuesta aceptada debe actualizarse a esta.
jbowman
80

Como dijo Mike Samuel, no use jQuery.html (). Text () para decodificar entidades html ya que no es seguro.

En su lugar, use un renderizador de plantillas como Moustache.js o decodeEntities del comentario de @ VyvIT.

Underscore.js biblioteca de utilidades de correa viene con escapey unescapemétodos, pero que no son seguros para la entrada del usuario:

_.escape (cadena)

_.unescape (cadena)

Alan Hamlett
fuente
2
¡Esto realmente merece muchos más votos a favor! Definitivamente mi solución preferida. Incluidos unescapeen los documentos por ahora, por cierto.
guitarra letal
55
_.unescape("&#39;")da como resultado solo "& # 39;" en lugar de una comilla simple. ¿Hay algo que me falta o el subrayado no se escapa a los códigos de entidad HTML como se muestra en: w3schools.com/tags/ref_entities.asp
Jason Axelson
66
El error en github se cerró como "No se solucionará"; eso significa que esta solución no funciona y no funcionará.
Igor Chubin
3
Usted dice que " escapey los unescapemétodos ... no son seguros para la entrada del usuario" . ¿Qué quiere decir con esto? A mí me parece una tontería, pero tal vez me estoy perdiendo algo, ¿puedes aclararlo?
Mark Amery
2
@VyvIT Probado _.unescape("&lt;img src=fake onerror=alert('boo!')&gt;")(en Chrome / FF / IE). Pero no apareció ninguna alerta. Lo probé en la consola y también lo puse en mi archivo JS. Mismo resultado.
Vivek Athalye
28

Creo que estás confundiendo los métodos de texto y HTML. Mire este ejemplo, si usa el HTML interno de un elemento como texto, obtendrá etiquetas HTML decodificadas (segundo botón). Pero si los usa como HTML, obtendrá la vista con formato HTML (primer botón).

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

Primer botón escribe: aquí hay un contenido HTML .

El segundo botón escribe: aquí hay un contenido <B> HTML </B>.

Por cierto, puede ver un complemento que encontré en el complemento jQuery: decodificación y codificación HTML que codifica y decodifica cadenas HTML.

Canavar
fuente
26

La pregunta está limitada por 'con jQuery', pero podría ayudar a algunos saber que el código jQuery que figura en la mejor respuesta aquí hace lo siguiente debajo ... esto funciona con o sin jQuery:

function decodeEntities(input) {
  var y = document.createElement('textarea');
  y.innerHTML = input;
  return y.value;
}
Rondó
fuente
20

Puede usar la biblioteca he , disponible en https://github.com/mathiasbynens/he

Ejemplo:

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

Me reté autor de la biblioteca sobre la cuestión de si había alguna razón para utilizar esta biblioteca en el código del lado del cliente a favor del <textarea>corte previsto en otras respuestas , aquí y en otros lugares. Proporcionó algunas justificaciones posibles:

  • Si está utilizando el servidor de node.js, el uso de una biblioteca para la codificación / decodificación HTML le brinda una solución única que funciona tanto en el lado del cliente como en el servidor.

  • Algunos algoritmos de decodificación de entidades de los navegadores tienen errores o les falta soporte para algunas referencias de caracteres con nombre . Por ejemplo, Internet Explorer decodificará y renderizará espacios que no se rompen ( &nbsp;) correctamente, pero los informará como espacios ordinarios en lugar de espacios que no se rompen a través de la innerTextpropiedad de un elemento DOM , rompiendo el <textarea>pirateo (aunque solo de una manera menor). Además, IE 8 y 9 simplemente no admiten ninguna de las nuevas referencias de caracteres con nombre agregadas en HTML 5. El autor de él también presenta una prueba de compatibilidad con referencias de caracteres con nombre en http://mathias.html5.org/tests/html / referencias-caracteres-nombrados / . En IE 8, informa más de mil errores.

    Si desea aislarse de los errores del navegador relacionados con la decodificación de entidades y / o poder manejar la gama completa de referencias de caracteres con nombre, no puede salirse con la suya <textarea>; Necesitarás una biblioteca como él .

  • Simplemente siente que hacer las cosas de esta manera es menos hacky.

Mark Amery
fuente
44
+1 jQuery no es la solución para todo. Use la herramienta adecuada para el trabajo.
Mathias Bynens
Esta es la mejor manera de decodificar entidades HTML. Todas las demás respuestas (sobre esta y otras preguntas similares) usan innerHTML (crea un nuevo elemento HTML, procesa el código HTML y luego obtiene innerHTML de ese elemento, esto puede ser vulnerable a los ataques XSS si no eres MUY cuidadoso, ver más ), o sugieren el uso de Underscore.js unescape o Lodash unescape métodos que son a la vez incompleta (funciona sólo para unos entidades HTML). ¡La biblioteca he es la opción más completa y segura!
ands
18

codificar:

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

descodificar:

$("<textarea/>").html('&lt;a&gt').val()   // return '<a>'
usuario4064396
fuente
3
Ya hay una respuesta que funciona, y es casi idéntica a esta. No necesitamos respuestas duplicadas
markasoftware
44
Esta es la respuesta válida. La respuesta de tom utiliza un elemento DIV, lo que hace que esa respuesta sea vulnerable a XSS.
Francisco Hodge
2
Esta es la mejor respuesta para mayor claridad.
Dan Randolph
4

Utilizar

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

Es más fácil hacerlo en el lado del servidor porque aparentemente JavaScript no tiene una biblioteca nativa para manejar entidades, ni encontré ninguna cerca de la parte superior de los resultados de búsqueda para los diversos marcos que extienden JavaScript.

Busque "entidades HTML JavaScript", y puede encontrar algunas bibliotecas para ese propósito, pero probablemente todas se construirán en torno a la lógica anterior: reemplazar, entidad por entidad.

Peter Mortensen
fuente
0

Solo tenía que tener un carácter de entidad HTML (⇓) como valor para un botón HTML. El código HTML se ve bien desde el principio en el navegador:

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

Ahora estaba agregando una palanca que también debería mostrar el personaje. Esta es mi solucion

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

Esto muestra ⇓ nuevamente en el botón. Espero que esto pueda ayudar a alguien.

philipp
fuente
Más simple sería usar una secuencia de escape unicode (es decir "Embed & Share \u21d1"), o mejor aún, solo "Embed & Share ⇑"si puede servir su script en UTF-8 (o UTF-16, o cualquier otra codificación que admita el carácter ⇑). El uso de un elemento DOM para analizar una entidad HTML solo para hornear un carácter unicode arbitrario en una cadena de JavaScript es un enfoque astuto y creativo que enorgullecería a Rube Goldberg, pero no es una buena práctica; los escapes de Unicode están en el lenguaje específicamente para manejar este caso de uso.
Mark Amery
0

Debe hacer una función personalizada para las entidades html:

function htmlEntities(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g,'&gt;').replace(/"/g, '&quot;');
}
Ali
fuente
No tengo idea, me ayudó mucho +1 l-)
Szymon Toda
posiblemente fue rechazado porque solo maneja algunas entidades.
Jasen
La pregunta original era cómo decodificar entidades: esto hace lo contrario de lo que se desea; que codifica un conjunto extremadamente limitado de caracteres en entidades. Como dice la sugerencia de voto negativo, "esta respuesta no es útil". Me sorprende que después de 4 años todavía tenga una puntuación neta positiva.
Stephen P
0

Supongamos que tienes debajo de String.

Nuestras cabañas de lujo son cálidas, acogedoras y amplias; cómodo

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

str y asignar de nuevo a

etiqueta.

Eso es.

Anirudh Sood
fuente
0

Para usuarios de ExtJS, si ya tiene la cadena codificada, por ejemplo, cuando el valor devuelto de una función de biblioteca es el contenido innerHTML, considere esta función ExtJS:

Ext.util.Format.htmlDecode(innerHtmlContent)
Ilan
fuente
Esto solo funcionará para 5 entidades HTML. Puede ver esto en la documentación y el código fuente .
yS¯
0

Extender una clase de cadena:

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

y usar como método:

"&lt;img src='myimage.jpg'&gt;".decode()
Sergio Belevskij
fuente
0

Prueba esto :

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 es una función en la biblioteca Jquery y devolverá una matriz que incluye algunos detalles sobre la cadena dada.

en algunos casos, la cadena es grande, por lo que la función separará el contenido en muchos índices.

y para obtener todos los datos de los índices, debe ir a cualquier índice y luego acceder al índice llamado "wholeText".

Elegí el índice 0 porque funcionará en todos los casos (cadena pequeña o cadena grande).

Fawaz Al Romy
fuente
Si bien este fragmento de código puede ser la solución, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y que esas personas podrían no saber los motivos de su sugerencia de código.
Johan
Se agrega la explicación ... Gracias :)
Fawaz Al Romy
-1

Todavía hay un problema: la cadena de escape no parece legible cuando se asigna al valor de entrada

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

Exapmle: https://jsfiddle.net/kjpdwmqa/3/

Lauris Kuznecovs
fuente
Esta no es una respuesta a la pregunta. OP pide decodificar (no escapar) la entidad HTML, pero en esta respuesta está utilizando el escapemétodo de Underscore.js. Tampoco hay explicación de cómo su ejemplo de código debería resolver el problema de OP.
yS¯
-1

Alternativamente, también hay una biblioteca para ello.

aquí, https://cdnjs.com/libraries/he

npm install he                 //using node.js

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

El uso es el siguiente ...

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

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

salud.

Ande Caleb
fuente
Ya hay una respuesta sobre la biblioteca que está completa, con un ejemplo de código simple y una buena explicación de por qué y cuándo debe usar la biblioteca .
yS¯
-3

Para decodificar entidades HTML con jQuery, solo use esta función:

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;
}

Cómo utilizar:

Javascript:

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

HTML:

<input id="some-id" type="text" />
Fred
fuente
-3

La forma más fácil es establecer un selector de clase para sus elementos y luego usar el siguiente código:

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

¡Nada más necesario!

Tuve este problema y encontré esta solución clara y funciona bien.

Hamidreza
fuente
Esta no es una respuesta a la pregunta de OP. OP pide decodificar entidades HTML en STRING, no solo esto no resuelve el problema del OP sino que también reemplaza las entidades HTML escapadas en el elemento HTML con otras no escaneadas que no deberían hacerse.
yS¯
-3

Creo que es exactamente lo contrario de la solución elegida.

var decoded = $("<div/>").text(encodedStr).html();
Pedro
fuente