Как декодировать HTML-сущности с помощью jQuery?

334

Как я могу использовать jQuery для декодирования HTML-объектов в строке?

EddyR
источник
Преждевременный выбор технологии (jQuery) предлагает ответы с проблемами безопасности. Это может быть лучше закрывать как дубликат stackoverflow.com/questions/1912501/… .
Владимир Палант

Ответы:

437

Примечание по безопасности: использование этого ответа (сохранено в его первоначальном виде ниже) может привести к уязвимости XSS в вашем приложении. Вы не должны использовать этот ответ. Прочитайте ответ Лукашаро, чтобы объяснить уязвимости в этом ответе, и вместо этого используйте подход из этого ответа или ответа Марка Эмери .

На самом деле, попробуйте

var decoded = $("<div/>").html(encodedStr).text();
Том
источник
175
Как не сделать это с ненадежными данными. Многие браузеры загружают изображения и запускают связанные события, даже если узел не подключен к DOM. Попробуйте запустить $("<div/>").html('<img src="http://www.google.com/images/logos/ps_logo2.png" onload=alert(1337)>'). В Firefox или Safari выдает предупреждение.
Майк Сэмюэл
@ Майк, так что ты порекомендовал бы вместо этого? ваш ответ .replace () не годится, если вы не знаете, что заменяете ...
ekkis
7
@ekkis, вам нужно удалить теги, прежде чем пытаться декодировать объекты. str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/g, "")или что-то подобное.
Майк Сэмюэл
2
Лучшая реализация (на мой взгляд), которая удаляет большинство тегов HTML (любезно предоставлено Майком) из входных данных, - это мой ответ на аналогичный вопрос . Он также не имеет накладных расходов jQuery, поэтому он вполне подходит для других сред.
Роберт К
6
@MichaelStum ваше редактирование здесь лишило законной силы и комментарий Майка Сэмюэля, и ответ, получивший следующий голос, и сделал это без фактического устранения уязвимости XSS для всех версий jQuery (как объяснено в ответе ниже). Было бы разумно добавить предупреждение безопасности к этому ответу (и я собираюсь это сделать); делать другие обсуждения на этой странице бессмысленными, в то время как неспособность фактически устранить дыру в безопасности определенно не является!
Марк Амери
211

Без каких-либо jQuery:

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

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

Это работает аналогично принятому ответу , но безопасно для использования с ненадежным пользовательским вводом.


Проблемы безопасности в аналогичных подходах

Как отметил Майк Самуэль , выполнение этого с использованием ненадежного пользовательского ввода <div>вместо <textarea>уязвимости XSS, даже если <div>оно никогда не добавляется в 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)">')

Тем не менее, эта атака невозможна против a, <textarea>потому что нет элементов HTML, которым разрешено содержимое a <textarea>. Следовательно, любые HTML-теги, все еще присутствующие в «кодированной» строке, будут автоматически кодироваться сущностью браузером.

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

Предупреждение : Делать это, используя jQuery .html()и .val()методы вместо использования, .innerHTMLа .valueтакже небезопасно * для некоторых версий jQuery, даже при использовании atextarea . Это связано с тем, что более старые версии jQuery сознательно и явно оценивают сценарии, содержащиеся в передаваемой строке .html(). Следовательно, такой код показывает предупреждение в 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>

* Спасибо Эру Пенкману за обнаружение этой уязвимости.

lucascaro
источник
6
Может быть хорошей идеей уничтожить текстовую область после извлечения ее значения:decodedString = textArea.value; textArea.remove(); return decodedString;
Вернер
2
Или только если версия javascript действительно поддерживает remove ():if ('remove' in Element.prototype) textArea.remove();
Вернер
6
@Werner После выхода из функции больше не будет переменных, содержащих ссылку на нее, поэтому она будет автоматически удалена сборщиком мусора .
user2428118
Я использую это в сочетании с .NET из-за нажатия кнопки, и по какой-то причине принятый ответ вызвал обратную передачу. Такого ответа не было, так что это лучший ответ для меня. Спасибо!
Snailer
@Snailer $("<div />").html(string).text() выполнит любой javascript в предоставленной строке , что, я подозреваю, является причиной вашей проблемы. Принятый ответ должен быть обновлен до этого.
jbowman
80

Как сказал Майк Сэмюэл, не используйте jQuery.html (). Text () для декодирования html-объектов, поскольку это небезопасно.

Вместо этого используйте средство визуализации шаблонов, такое как Mustache.js или decodeEntities из комментария @ VyvIT.

Underscore.jsБиблиотека утилит-пояса поставляется с escapeи unescapeметодами, но они небезопасны для ввода пользователем:

_.escape (строка)

_.unescape (строка)

Алан Хамлетт
источник
2
Это на самом деле заслуживает гораздо больше голосов! Определенно мое предпочтительное решение. К настоящему времени они включены unescapeв документы, кстати.
Смертельная гитара
5
_.unescape("&#39;")в результате просто "& # 39;" вместо одиночной кавычки. Есть ли что-то, что я пропускаю или подчеркиваю, не скрываются ли
Джейсон Аксельсон
6
Ошибка на github была закрыта как "Не исправлю"; это означает, что это решение не работает и не будет работать.
Игорь Чубин
3
Вы говорите, что Underscore " escapeи unescapeметоды ... небезопасны для пользовательского ввода" . Что вы под этим подразумеваете? Для меня это звучит чепухой, но, может быть, я что-то упустил - вы можете уточнить?
Марк Амери
2
@VyvIT Пробовал _.unescape("&lt;img src=fake onerror=alert('boo!')&gt;")(в Chrome / FF / IE). Но он не обнаружил никакого предупреждения. Пробовал в консоли, а также положить в мой файл JS тоже. Тот же результат.
Вивек Аталье
28

Я думаю, что вы путаете текст и методы HTML. Посмотрите на этот пример, если вы используете внутренний HTML-код элемента в качестве текста, вы получите декодированные HTML-теги (вторая кнопка). Но если вы используете их как HTML, вы получите представление в формате HTML (первая кнопка).

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

Первая кнопка пишет: вот HTML- контент.

Вторая кнопка пишет: вот содержимое <B> HTML </ B>.

Кстати, вы можете увидеть плагин, который я нашел в плагине jQuery - HTML декодировать и кодировать, который кодирует и декодирует HTML-строки.

Canavar
источник
26

Вопрос ограничен «с помощью jQuery», но это может помочь некоторым узнать, что код jQuery, приведенный в лучшем ответе здесь, выполняет следующее ниже ... это работает с или без jQuery:

function decodeEntities(input) {
  var y = document.createElement('textarea');
  y.innerHTML = input;
  return y.value;
}
Рондо
источник
20

Вы можете использовать библиотеку he , доступную по адресу 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"

Я обратился к автору библиотеки с вопросом о том, была ли какая-либо причина использовать эту библиотеку в клиентском коде в пользу <textarea>взлома, приведенного в других ответах здесь и в других местах. Он представил несколько возможных оправданий:

  • Если вы используете server.js на стороне сервера, использование библиотеки для кодирования / декодирования HTML дает вам единственное решение, которое работает как на стороне клиента, так и на стороне сервера.

  • Алгоритмы декодирования объектов в некоторых браузерах содержат ошибки или не поддерживают некоторые ссылки на именованные символы . Например, Internet Explorer будет &nbsp;правильно декодировать и отображать неразрывные пробелы ( ), но сообщать о них как об обычных пробелах, а не неразрывных через innerTextсвойство элемента DOM , нарушая <textarea>взлом (хотя и незначительным образом). Кроме того, IE 8 и 9 просто не поддерживает какой - либо из новых именованных ссылок на символы , добавленные в HTML 5. Автор он также проводит проверку по имени справочной поддержки символов в http://mathias.html5.org/tests/html / именованные символьные ссылки / . В IE 8 сообщается более тысячи ошибок.

    Если вы хотите быть изолированными от ошибок браузера, связанных с декодированием сущностей и / или иметь возможность обрабатывать полный диапазон именованных ссылок на символы, вы не можете избежать неприятностей <textarea>; вам понадобится библиотека, как он .

  • Он просто чертовски хорош, чувствует себя так, как будто не так хакерски.

Марк Эмери
источник
4
+1 jQuery - это не решение для всего. Используйте правильный инструмент для работы.
Матиас Биненс
Это лучший способ декодирования HTML-объектов. Все остальные ответы (на этот и аналогичные вопросы) либо используют innerHTML (создают новый элемент HTML, обрабатывают код HTML, а затем получают innerHTML этого элемента, это может быть уязвимо для атак XSS, если вы ОЧЕНЬ осторожны, см. Подробнее ), или они предлагают использовать Underscore.js экранирования в или Lodash экранирования в методы , которые одновременно являются неполными (работает только для нескольких HTML - сущностей). Библиотека является наиболее полным и безопасным вариантом!
Ands
18

закодировать:

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

расшифровывает:

$("<textarea/>").html('&lt;a&gt').val()   // return '<a>'
user4064396
источник
3
уже есть ответ, который работает, и он почти идентичен этому. Нам не нужны повторяющиеся ответы
markasoftware
4
Это правильный ответ. В ответе Тома используется элемент DIV, что делает этот ответ уязвимым для XSS.
Франциско Ходж
2
Это лучший ответ для ясности.
Дэн Рэндольф
4

использование

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

Проще всего сделать это на стороне сервера, потому что, очевидно, JavaScript не имеет встроенной библиотеки для обработки сущностей, и я не нашел ничего в верхней части результатов поиска для различных сред, расширяющих JavaScript.

Ищите «JavaScript HTML-сущности», и вы можете найти несколько библиотек именно для этой цели, но, вероятно, все они будут построены на основе вышеупомянутой логики - заменить сущность на сущность.

Питер Мортенсен
источник
0

Мне просто нужно было использовать символ сущности HTML (⇓) в качестве значения кнопки HTML. HTML-код выглядит хорошо с самого начала в браузере:

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

Теперь я добавил переключатель, который также должен отображать характер. Это мое решение

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

На дисплее снова отобразится ⇓. Я надеюсь, что это может кому-то помочь.

Philipp
источник
Проще было бы использовать escape-последовательность Unicode (то есть "Embed & Share \u21d1"), или еще лучше, просто "Embed & Share ⇑"если вы можете обслуживать свой сценарий в UTF-8 (или UTF-16, или в любой другой кодировке, поддерживающей символ ⇑). Использование элемента DOM для анализа HTML-сущности просто для того, чтобы запрограммировать произвольный символ Юникода в строку JavaScript, - это хитрый и творческий подход, который мог бы гордить Рубе Голдберга, но не является хорошей практикой; экранирование Юникода на языке специально для обработки этого варианта использования.
Марк Эмери
0

Вы должны сделать пользовательскую функцию для объектов HTML:

function htmlEntities(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g,'&gt;').replace(/"/g, '&quot;');
}
Али
источник
Понятия не имею, мне это так помогло +1 л-)
Шимон Тода
возможно, за него проголосовали, потому что он обрабатывает только некоторые объекты.
Ясен
Первоначальный вопрос заключался в том, как декодировать объекты - это противоположно тому, что требуется; он кодирует чрезвычайно ограниченный набор символов в сущности. Как подсказка при голосовании говорит: «Этот ответ бесполезен». Я удивлен, что через 4 года он все еще имеет чистый положительный результат.
Стивен П.
0

Предположим, у вас ниже строки.

Наши роскошные каюты теплые, уютные и удобный

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

ул и назначить обратно

тег.

Это оно.

Анируд Суд
источник
0

Для пользователей ExtJS, если у вас уже есть закодированная строка, например, когда возвращаемое значение библиотечной функции является содержимым innerHTML, рассмотрите эту функцию ExtJS:

Ext.util.Format.htmlDecode(innerHtmlContent)
Илан
источник
Это будет работать только для 5 объектов HTML. Вы можете увидеть это в документации и исходном коде .
Ands
0

Расширить класс String:

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

и использовать в качестве метода:

"&lt;img src='myimage.jpg'&gt;".decode()
Серхио Белевский
источник
0

Попробуй это :

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 - это функция из библиотеки Jquery, которая возвращает массив, содержащий некоторые сведения о данной строке.

в некоторых случаях значение String велико, поэтому функция будет разделять содержимое на множество индексов.

и чтобы получить все данные индексов, вы должны перейти к любому индексу, а затем получить доступ к индексу, называемому «целым текстом».

Я выбрал индекс 0, потому что он будет работать во всех случаях (маленькая строка или большая строка).

Фаваз Аль Роми
источник
Хотя этот фрагмент кода может быть решением, включение пояснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете код.
Йохан
Fawaz Al Romy
-1

Вот еще одна проблема: Экранированная строка не выглядит читаемой при назначении входного значения

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

Пример: https://jsfiddle.net/kjpdwmqa/3/

Лаурис Кузнецов
источник
Это не ответ на вопрос. OP просит декодировать (unescape) HTML-сущность, но в этом ответе вы используете escapeметод Underscore.js. Также нет объяснения, как ваш пример кода должен решить проблему OP.
ANDS
-1

Кроме того, есть библиотека для этого ..

здесь, https://cdnjs.com/libraries/he

npm install he                 //using node.js

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

Использование заключается в следующем ...

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

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

веселит.

Анд Калеб
источник
Уже есть ответ о библиотеке, который завершен, с простым примером кода и хорошим объяснением, почему и когда вы должны использовать библиотеку .
Ands
-3

Чтобы декодировать HTML-объекты с помощью jQuery, просто используйте эту функцию:

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

Как пользоваться:

Javascript:

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

HTML:

<input id="some-id" type="text" />
Фред
источник
-3

Самый простой способ - установить селектор класса на ваши элементы, а затем использовать следующий код:

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

Больше ничего не нужно!

У меня была эта проблема, и я нашел это ясное решение, и оно отлично работает.

Hamidreza
источник
Это не ответ на вопрос ОП. OP просит декодировать HTML-сущности в STRING, не только это не решает проблему OP, но также заменяет экранированные HTML-сущности в элементе HTML неподтвержденными, что не должно быть сделано.
Ands
-3

Я думаю, что это полная противоположность выбранному решению.

var decoded = $("<div/>").text(encodedStr).html();
Pedro
источник