Unescape HTML thực thể trong Javascript?


177

Tôi có một số mã Javascript giao tiếp với phụ trợ XML-RPC. XML-RPC trả về các chuỗi có dạng:

<img src='myimage.jpg'>

Tuy nhiên, khi tôi sử dụng Javascript để chèn các chuỗi vào HTML, chúng sẽ hiển thị theo đúng nghĩa đen. Tôi không thấy một hình ảnh, tôi thực sự thấy chuỗi:

<img src='myimage.jpg'>

Tôi đoán là HTML đang được thoát qua kênh XML-RPC.

Làm cách nào tôi có thể hủy bỏ chuỗi trong Javascript? Tôi đã thử các kỹ thuật trên trang này, không thành công: http://paulschreiber.com/blog/2008/09/20/javascript-how-to-unescape-html-entities/

Những cách khác để chẩn đoán vấn đề là gì?


1
Hãy thử điều này: stackoverflow.com/questions/4480757/
Mạnh

Câu trả lời:


177

EDIT: Bạn nên sử dụng API DOMParser như Wladimir gợi ý , tôi đã chỉnh sửa câu trả lời trước đó của mình vì chức năng được đăng đã giới thiệu một lỗ hổng bảo mật.

Đoạn mã sau đây là mã của câu trả lời cũ với một sửa đổi nhỏ: sử dụng textareathay vì divgiảm lỗ hổng XSS, nhưng nó vẫn có vấn đề trong IE9 và Firefox.

function htmlDecode(input){
  var e = document.createElement('textarea');
  e.innerHTML = input;
  // handle case of empty input
  return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}

htmlDecode("&lt;img src='myimage.jpg'&gt;"); 
// returns "<img src='myimage.jpg'>"

Về cơ bản, tôi tạo một phần tử DOM theo chương trình, gán HTML được mã hóa vào bên trongHTML của nó và truy xuất nodeValue từ nút văn bản được tạo trên phần chèn trongHTML. Vì nó chỉ tạo một phần tử nhưng không bao giờ thêm nó, nên không có HTML trang web nào được sửa đổi.

Nó sẽ hoạt động trên nhiều trình duyệt (bao gồm các trình duyệt cũ hơn) và chấp nhận tất cả các Thực thể Ký tự HTML .

EDIT: Phiên bản cũ của mã này không hoạt động trên IE với các đầu vào trống, như đã được chứng minh ở đây trên jsFiddle (xem trong IE). Phiên bản trên hoạt động với tất cả các đầu vào.

CẬP NHẬT: xuất hiện điều này không hoạt động với chuỗi lớn và nó cũng giới thiệu một lỗ hổng bảo mật , xem bình luận.


Hiểu rồi, bạn đã đổi thành ', vì vậy hãy để tôi xóa bình luận của tôi trở lại, thx, nó hoạt động rất tốt, +1
BẠN

1
@ S.Mark: &apos;không thuộc về Thực thể HTML 4, đó là lý do! w3.org/TR/html4/sgml/entities.html fishbowl.pastiche.org/2003/07/01/the_curse_of_apos
CMS

2
Xem thêm lưu ý của @ kender về tính bảo mật kém của phương pháp này.
Joseph Turian

2
Xem ghi chú của tôi cho @kender về bài kiểm tra kém mà anh ấy đã làm;)
Roatin Marth

24
Hàm này là một mối nguy hiểm về bảo mật, mã JavaScript sẽ chạy ngay cả khi phần tử không được thêm vào DOM. Vì vậy, đây chỉ là một cái gì đó để sử dụng nếu chuỗi đầu vào được tin cậy. Tôi đã thêm câu trả lời của riêng mình để giải thích vấn đề và cung cấp một giải pháp an toàn. Là một tác dụng phụ, kết quả sẽ không bị cắt nếu có nhiều nút văn bản tồn tại.
Wladimir Palant

375

Hầu hết các câu trả lời được đưa ra ở đây đều có một nhược điểm rất lớn: nếu chuỗi bạn đang cố gắng chuyển đổi không đáng tin cậy thì bạn sẽ bị lỗ hổng XSS (Cross-Site Scripting) . Đối với chức năng trong câu trả lời được chấp nhận , hãy xem xét những điều sau đây:

htmlDecode("<img src='dummy' onerror='alert(/xss/)'>");

Chuỗi ở đây chứa thẻ HTML không thoát, vì vậy thay vì giải mã bất cứ điều gì, htmlDecodehàm thực sự sẽ chạy mã JavaScript được chỉ định bên trong chuỗi.

Điều này có thể tránh được bằng cách sử dụng DOMParser được hỗ trợ trong tất cả các trình duyệt hiện đại :

function htmlDecode(input) {
  var doc = new DOMParser().parseFromString(input, "text/html");
  return doc.documentElement.textContent;
}

console.log(  htmlDecode("&lt;img src='myimage.jpg'&gt;")  )    
// "<img src='myimage.jpg'>"

console.log(  htmlDecode("<img src='dummy' onerror='alert(/xss/)'>")  )  
// ""

Hàm này được đảm bảo không chạy bất kỳ mã JavaScript nào dưới dạng tác dụng phụ. Mọi thẻ HTML sẽ bị bỏ qua, chỉ nội dung văn bản sẽ được trả về.

Lưu ý tương thích : Phân tích HTML với DOMParseryêu cầu ít nhất Chrome 30, Firefox 12, Opera 17, Internet Explorer 10, Safari 7.1 hoặc Microsoft Edge. Vì vậy, tất cả các trình duyệt không có hỗ trợ đều vượt qua EOL của chúng và vào năm 2017, những trình duyệt duy nhất vẫn có thể thấy trong tự nhiên đôi khi là các phiên bản Internet Explorer và Safari cũ hơn (thường thì chúng vẫn không đủ để làm phiền).


19
Tôi nghĩ rằng câu trả lời này là tốt nhất vì nó đã đề cập đến lỗ hổng XSS.
Toàn cảnh

2
Lưu ý rằng (theo tham chiếu của bạn) DOMParserkhông hỗ trợ "text/html"trước Firefox 12.0 và vẫn còn một số phiên bản trình duyệt mới nhất thậm chí không hỗ trợDOMParser.prototype.parseFromString() . Theo tài liệu tham khảo của bạn, DOMParservẫn là một công nghệ thử nghiệm và các công cụ độc lập sử dụng thuộc innerHTMLtính, như bạn cũng đã chỉ ra để đáp lại cách tiếp cận của tôi , có lỗ hổng XSS này (phải được sửa bởi các nhà cung cấp trình duyệt).
PointedEars

4
@PointedEars: Ai quan tâm đến Firefox 12 năm 2016? Những vấn đề nan giải là Internet Explorer lên tới 9.0 và Safari lên tới 7.0. Nếu một người có thể không đủ khả năng không hỗ trợ họ (hy vọng sẽ sớm có mặt mọi người) thì DOMParser là lựa chọn tốt nhất. Nếu không - có, xử lý thực thể chỉ là một tùy chọn.
Wladimir Palant

4
@PointedEars: <script>các thẻ không được thực thi không phải là một cơ chế bảo mật, quy tắc này chỉ tránh các vấn đề thời gian khó khăn nếu cài đặt innerHTMLcó thể chạy các tập lệnh đồng bộ như một tác dụng phụ. Vệ sinh mã HTML là một vấn đề phức tạp và innerHTMLthậm chí không thử - vì trang web thực sự có thể có ý định thiết lập các trình xử lý sự kiện nội tuyến. Đây đơn giản không phải là một cơ chế dành cho dữ liệu không an toàn, dừng hoàn toàn.
Wladimir Palant

1
@ ИИяяяя я::: Bạn có kế hoạch sử dụng mã này trong một vòng lặp chặt chẽ hay tại sao hiệu suất lại quan trọng? Câu trả lời của bạn một lần nữa dễ bị tổn thương với XSS, nó có thực sự đáng không?
Wladimir Palant

37

Nếu bạn đang sử dụng jQuery:

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

Mặt khác, sử dụng Đối tượng mã hóa của Strictly Software , có htmlDecode()chức năng tuyệt vời .


59
Không (lặp lại KHÔNG) sử dụng nội dung này cho nội dung do người dùng tạo ngoài nội dung do người dùng này tạo . Nếu có thẻ <script> trong giá trị, nội dung của tập lệnh sẽ được thực thi!
Malvolio

Tôi không thể tìm thấy giấy phép cho bất cứ nơi nào trên trang web. Bạn có biết giấy phép là gì?
TRiG

Có một giấy phép trong tiêu đề nguồn, đó là GPL.
Chris Fulstow

6
CÓ, chức năng đó mở đường cho XSS: thử htmlDecode ("<script> alert (12) </ script> 123 & gt;")
Dinis Cruz

Ý nghĩa của $ ('<div />') là gì?
Tiếng vọng

13

Mẹo nhỏ là sử dụng sức mạnh của trình duyệt để giải mã các ký tự HTML đặc biệt, nhưng không cho phép trình duyệt thực thi các kết quả như thể đó là html thực tế ... Hàm này sử dụng biểu thức chính quy để xác định và thay thế các ký tự HTML được mã hóa, một ký tự tại một thời điểm.

function unescapeHtml(html) {
    var el = document.createElement('div');
    return html.replace(/\&[#0-9a-z]+;/gi, function (enc) {
        el.innerHTML = enc;
        return el.innerText
    });
}

Regex có thể được kết hợp chặt chẽ hơn một chút /\&#?[0-9a-z]+;/givì # chỉ nên xuất hiện dưới dạng ký tự thứ 2 nếu có.
TheAtomicOption

Đây là câu trả lời tốt nhất. Tránh lỗ hổng XSS và không tước thẻ HTML.
Emmanuel

6

Câu trả lời của CMS hoạt động tốt, trừ khi HTML bạn muốn hủy hiển thị rất dài, dài hơn 65536 ký tự. Bởi vì sau đó trong Chrome, HTML bên trong được chia thành nhiều nút con, mỗi nút dài tối đa 65536 và bạn cần nối chúng lại với nhau. Hàm này cũng hoạt động với các chuỗi rất dài:

function unencodeHtmlContent(escapedHtml) {
  var elem = document.createElement('div');
  elem.innerHTML = escapedHtml;
  var result = '';
  // Chrome splits innerHTML into many child nodes, each one at most 65536.
  // Whereas FF creates just one single huge child node.
  for (var i = 0; i < elem.childNodes.length; ++i) {
    result = result + elem.childNodes[i].nodeValue;
  }
  return result;
}

Xem câu trả lời này về innerHTMLđộ dài tối đa để biết thêm: https://stackoverflow.com/a/27545633/694469


3

Không phải là câu trả lời trực tiếp cho câu hỏi của bạn, nhưng sẽ tốt hơn nếu RPC của bạn trả lại một số cấu trúc (có thể là XML hoặc JSON hoặc bất cứ thứ gì) với các dữ liệu hình ảnh đó (trong ví dụ của bạn) trong cấu trúc đó?

Sau đó, bạn có thể phân tích cú pháp trong javascript của mình và xây dựng <img>bằng cách sử dụng javascript.

Cấu trúc bạn nhận được từ RPC có thể trông như sau:

{"img" : ["myimage.jpg", "myimage2.jpg"]}

Tôi nghĩ rằng nó tốt hơn theo cách này, vì việc tiêm mã từ nguồn bên ngoài vào trang của bạn trông không an toàn lắm. Hình ảnh ai đó chiếm quyền điều khiển tập lệnh XML-RPC của bạn và đặt thứ gì đó bạn không muốn vào đó (thậm chí một số javascript ...)


Cách tiếp cận @CMS ở trên có lỗ hổng bảo mật này không?
Joseph Turian

Tôi vừa kiểm tra đối số sau đây được chuyển đến phiên bản htmlDecode: htmlDecode ("& lt; img src = 'myimage.jpg' & gt; & lt; script & gt; document.write ('xxxxx'); & lt; / script & gt;") và nó tạo ra Phần tử <script> </ script> có thể xấu, imho. Và tôi vẫn nghĩ trả về một cấu trúc thay vì văn bản được chèn vào là tốt hơn, bạn có thể xử lý các lỗi độc đáo chẳng hạn.
kender

1
Tôi chỉ cố gắng htmlDecode("&lt;img src='myimage.jpg'&gt;&lt;script&gt;alert('xxxxx');&lt;/script&gt;")và không có gì xảy ra. Tôi đã nhận được chuỗi html giải mã trở lại như mong đợi.
Roatin Marth

2

Câu trả lời của Chris rất hay và thanh lịch nhưng không thành công nếu giá trị không được xác định . Chỉ cần cải tiến đơn giản làm cho nó vững chắc:

function htmlDecode(value) {
   return (typeof value === 'undefined') ? '' : $('<div/>').html(value).text();
}

Nếu cải thiện, hãy làm:return (typeof value !== 'string') ? '' : $('<div/>').html(value).text();
SynCap

2

Bạn được chào đón ... chỉ là một người đưa tin ... tín dụng đầy đủ sẽ được gửi tới ourcodeworld.com, liên kết bên dưới.

window.htmlentities = {
        /**
         * Converts a string to its html characters completely.
         *
         * @param {String} str String with unescaped HTML characters
         **/
        encode : function(str) {
            var buf = [];

            for (var i=str.length-1;i>=0;i--) {
                buf.unshift(['&#', str[i].charCodeAt(), ';'].join(''));
            }

            return buf.join('');
        },
        /**
         * Converts an html characterSet into its original character.
         *
         * @param {String} str htmlSet entities
         **/
        decode : function(str) {
            return str.replace(/&#(\d+);/g, function(match, dec) {
                return String.fromCharCode(dec);
            });
        }
    };

Tín dụng đầy đủ: https://ourcodeworld.com/articles/read/188/encode-and-decode-html-entities-USE-pure-javascript


2

Đây là giải pháp toàn diện nhất mà tôi đã thử cho đến nay:

const STANDARD_HTML_ENTITIES = {
    nbsp: String.fromCharCode(160),
    amp: "&",
    quot: '"',
    lt: "<",
    gt: ">"
};

const replaceHtmlEntities = plainTextString => {
    return plainTextString
        .replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec))
        .replace(
            /&(nbsp|amp|quot|lt|gt);/g,
            (a, b) => STANDARD_HTML_ENTITIES[b]
        );
};

"Toàn diện nhất"? Bạn đã thử chạy nó với một bộ thử nghiệm thực sự toàn diện chưa?
Dan Dascalescu

1

Tôi đã đủ điên rồ để trải qua và làm cho chức năng này trở nên đẹp, nếu không hoàn toàn, đầy đủ:

function removeEncoding(string) {
    return string.replace(/&Agrave;/g, "À").replace(/&Aacute;/g, "Á").replace(/&Acirc;/g, "Â").replace(/&Atilde;/g, "Ã").replace(/&Auml;/g, "Ä").replace(/&Aring;/g, "Å").replace(/&agrave;/g, "à").replace(/&acirc;/g, "â").replace(/&atilde;/g, "ã").replace(/&auml;/g, "ä").replace(/&aring;/g, "å").replace(/&AElig;/g, "Æ").replace(/&aelig;/g, "æ").replace(/&szlig;/g, "ß").replace(/&Ccedil;/g, "Ç").replace(/&ccedil;/g, "ç").replace(/&Egrave;/g, "È").replace(/&Eacute;/g, "É").replace(/&Ecirc;/g, "Ê").replace(/&Euml;/g, "Ë").replace(/&egrave;/g, "è").replace(/&eacute;/g, "é").replace(/&ecirc;/g, "ê").replace(/&euml;/g, "ë").replace(/&#131;/g, "ƒ").replace(/&Igrave;/g, "Ì").replace(/&Iacute;/g, "Í").replace(/&Icirc;/g, "Î").replace(/&Iuml;/g, "Ï").replace(/&igrave;/g, "ì").replace(/&iacute;/g, "í").replace(/&icirc;/g, "î").replace(/&iuml;/g, "ï").replace(/&Ntilde;/g, "Ñ").replace(/&ntilde;/g, "ñ").replace(/&Ograve;/g, "Ò").replace(/&Oacute;/g, "Ó").replace(/&Ocirc;/g, "Ô").replace(/&Otilde;/g, "Õ").replace(/&Ouml;/g, "Ö").replace(/&ograve;/g, "ò").replace(/&oacute;/g, "ó").replace(/&ocirc;/g, "ô").replace(/&otilde;/g, "õ").replace(/&ouml;/g, "ö").replace(/&Oslash;/g, "Ø").replace(/&oslash;/g, "ø").replace(/&#140;/g, "Œ").replace(/&#156;/g, "œ").replace(/&#138;/g, "Š").replace(/&#154;/g, "š").replace(/&Ugrave;/g, "Ù").replace(/&Uacute;/g, "Ú").replace(/&Ucirc;/g, "Û").replace(/&Uuml;/g, "Ü").replace(/&ugrave;/g, "ù").replace(/&uacute;/g, "ú").replace(/&ucirc;/g, "û").replace(/&uuml;/g, "ü").replace(/&#181;/g, "µ").replace(/&#215;/g, "×").replace(/&Yacute;/g, "Ý").replace(/&#159;/g, "Ÿ").replace(/&yacute;/g, "ý").replace(/&yuml;/g, "ÿ").replace(/&#176;/g, "°").replace(/&#134;/g, "†").replace(/&#135;/g, "‡").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&#177;/g, "±").replace(/&#171;/g, "«").replace(/&#187;/g, "»").replace(/&#191;/g, "¿").replace(/&#161;/g, "¡").replace(/&#183;/g, "·").replace(/&#149;/g, "•").replace(/&#153;/g, "™").replace(/&copy;/g, "©").replace(/&reg;/g, "®").replace(/&#167;/g, "§").replace(/&#182;/g, "¶").replace(/&Alpha;/g, "Α").replace(/&Beta;/g, "Β").replace(/&Gamma;/g, "Γ").replace(/&Delta;/g, "Δ").replace(/&Epsilon;/g, "Ε").replace(/&Zeta;/g, "Ζ").replace(/&Eta;/g, "Η").replace(/&Theta;/g, "Θ").replace(/&Iota;/g, "Ι").replace(/&Kappa;/g, "Κ").replace(/&Lambda;/g, "Λ").replace(/&Mu;/g, "Μ").replace(/&Nu;/g, "Ν").replace(/&Xi;/g, "Ξ").replace(/&Omicron;/g, "Ο").replace(/&Pi;/g, "Π").replace(/&Rho;/g, "Ρ").replace(/&Sigma;/g, "Σ").replace(/&Tau;/g, "Τ").replace(/&Upsilon;/g, "Υ").replace(/&Phi;/g, "Φ").replace(/&Chi;/g, "Χ").replace(/&Psi;/g, "Ψ").replace(/&Omega;/g, "Ω").replace(/&alpha;/g, "α").replace(/&beta;/g, "β").replace(/&gamma;/g, "γ").replace(/&delta;/g, "δ").replace(/&epsilon;/g, "ε").replace(/&zeta;/g, "ζ").replace(/&eta;/g, "η").replace(/&theta;/g, "θ").replace(/&iota;/g, "ι").replace(/&kappa;/g, "κ").replace(/&lambda;/g, "λ").replace(/&mu;/g, "μ").replace(/&nu;/g, "ν").replace(/&xi;/g, "ξ").replace(/&omicron;/g, "ο").replace(/&piρ;/g, "ρ").replace(/&rho;/g, "ς").replace(/&sigmaf;/g, "ς").replace(/&sigma;/g, "σ").replace(/&tau;/g, "τ").replace(/&phi;/g, "φ").replace(/&chi;/g, "χ").replace(/&psi;/g, "ψ").replace(/&omega;/g, "ω").replace(/&bull;/g, "•").replace(/&hellip;/g, "…").replace(/&prime;/g, "′").replace(/&Prime;/g, "″").replace(/&oline;/g, "‾").replace(/&frasl;/g, "⁄").replace(/&weierp;/g, "℘").replace(/&image;/g, "ℑ").replace(/&real;/g, "ℜ").replace(/&trade;/g, "™").replace(/&alefsym;/g, "ℵ").replace(/&larr;/g, "←").replace(/&uarr;/g, "↑").replace(/&rarr;/g, "→").replace(/&darr;/g, "↓").replace(/&barr;/g, "↔").replace(/&crarr;/g, "↵").replace(/&lArr;/g, "⇐").replace(/&uArr;/g, "⇑").replace(/&rArr;/g, "⇒").replace(/&dArr;/g, "⇓").replace(/&hArr;/g, "⇔").replace(/&forall;/g, "∀").replace(/&part;/g, "∂").replace(/&exist;/g, "∃").replace(/&empty;/g, "∅").replace(/&nabla;/g, "∇").replace(/&isin;/g, "∈").replace(/&notin;/g, "∉").replace(/&ni;/g, "∋").replace(/&prod;/g, "∏").replace(/&sum;/g, "∑").replace(/&minus;/g, "−").replace(/&lowast;/g, "∗").replace(/&radic;/g, "√").replace(/&prop;/g, "∝").replace(/&infin;/g, "∞").replace(/&OEig;/g, "Œ").replace(/&oelig;/g, "œ").replace(/&Yuml;/g, "Ÿ").replace(/&spades;/g, "♠").replace(/&clubs;/g, "♣").replace(/&hearts;/g, "♥").replace(/&diams;/g, "♦").replace(/&thetasym;/g, "ϑ").replace(/&upsih;/g, "ϒ").replace(/&piv;/g, "ϖ").replace(/&Scaron;/g, "Š").replace(/&scaron;/g, "š").replace(/&ang;/g, "∠").replace(/&and;/g, "∧").replace(/&or;/g, "∨").replace(/&cap;/g, "∩").replace(/&cup;/g, "∪").replace(/&int;/g, "∫").replace(/&there4;/g, "∴").replace(/&sim;/g, "∼").replace(/&cong;/g, "≅").replace(/&asymp;/g, "≈").replace(/&ne;/g, "≠").replace(/&equiv;/g, "≡").replace(/&le;/g, "≤").replace(/&ge;/g, "≥").replace(/&sub;/g, "⊂").replace(/&sup;/g, "⊃").replace(/&nsub;/g, "⊄").replace(/&sube;/g, "⊆").replace(/&supe;/g, "⊇").replace(/&oplus;/g, "⊕").replace(/&otimes;/g, "⊗").replace(/&perp;/g, "⊥").replace(/&sdot;/g, "⋅").replace(/&lcell;/g, "⌈").replace(/&rcell;/g, "⌉").replace(/&lfloor;/g, "⌊").replace(/&rfloor;/g, "⌋").replace(/&lang;/g, "⟨").replace(/&rang;/g, "⟩").replace(/&loz;/g, "◊").replace(/&#039;/g, "'").replace(/&amp;/g, "&").replace(/&quot;/g, "\"");
}

Được sử dụng như vậy:

let decodedText = removeEncoding("Ich hei&szlig;e David");
console.log(decodedText);

Bản in: Ich Heiße David

PS này mất khoảng một tiếng rưỡi để thực hiện.


0

Để hủy bỏ các thực thể HTML * trong JavaScript, bạn có thể sử dụng thư viện nhỏ html-escaper :npm install html-escaper

import {unescape} from 'html-escaper';

unescape('escaped string');

Hoặc unescapechức năng từ Lodash hoặc Underscore , nếu bạn đang sử dụng nó.


*) Xin vui lòng lưu ý rằng các chức năng này không bao gồm tất cả các thực thể HTML, nhưng chỉ những người phổ biến nhất, tức là &, <, >, ', ". Để unescape tất cả các thực thể HTML, bạn có thể sử dụng thư viện anh ấy .


-1

Tôi sử dụng điều này trong dự án của mình: lấy cảm hứng từ các câu trả lời khác nhưng với một tham số an toàn bổ sung, có thể hữu ích khi bạn xử lý các ký tự được trang trí

var decodeEntities=(function(){

    var el=document.createElement('div');
    return function(str, safeEscape){

        if(str && typeof str === 'string'){

            str=str.replace(/\</g, '&lt;');

            el.innerHTML=str;
            if(el.innerText){

                str=el.innerText;
                el.innerText='';
            }
            else if(el.textContent){

                str=el.textContent;
                el.textContent='';
            }

            if(safeEscape)
                str=str.replace(/\</g, '&lt;');
        }
        return str;
    }
})();

Và nó có thể sử dụng như:

var label='safe <b> character &eacute;ntity</b>';
var safehtml='<div title="'+decodeEntities(label)+'">'+decodeEntities(label, true)+'</div>';

-1

Tất cả các câu trả lời khác ở đây có vấn đề.

Các phương thức document.createEuity ('div') (bao gồm cả các phương thức sử dụng jQuery) thực thi bất kỳ javascript nào được truyền vào nó (một vấn đề bảo mật) và phương thức DOMParser.parseFromString () cắt ngang khoảng trắng. Đây là một giải pháp javascript thuần túy không có vấn đề gì:

function htmlDecode(html) {
    var textarea = document.createElement("textarea");
    html= html.replace(/\r/g, String.fromCharCode(0xe000)); // Replace "\r" with reserved unicode character.
    textarea.innerHTML = html;
    var result = textarea.value;
    return result.replace(new RegExp(String.fromCharCode(0xe000), 'g'), '\r');
}

TextArea được sử dụng cụ thể để tránh mã js thực thi. Nó vượt qua:

htmlDecode('&lt;&amp;&nbsp;&gt;'); // returns "<& >" with non-breaking space.
htmlDecode('  '); // returns "  "
htmlDecode('<img src="dummy" onerror="alert(\'xss\')">'); // Does not execute alert()
htmlDecode('\r\n') // returns "\r\n", doesn't lose the \r like other solutions.

1
Không, sử dụng một thẻ khác không giải quyết được vấn đề. Đây vẫn là một lỗ hổng XSS, hãy thử htmlDecode("</textarea><img src=x onerror=alert(1)>"). Bạn đã đăng bài này sau khi tôi đã chỉ ra vấn đề này trên câu trả lời của Sergio Belevskij.
Wladimir Palant

Tôi không thể tái tạo vấn đề mà bạn mô tả. Tôi có mã của bạn trong JsFiddle này và không có cảnh báo nào hiển thị khi chạy. jsfiddle.net/edsjt15g/1 Bạn có thể xem không? Bạn đang dùng trình duyệt nào?
EricP

2
Tôi đang sử dụng Firefox. Chrome thực sự xử lý tình huống này theo cách khác, do đó, mã không thực thi - tuy nhiên không phải là thứ bạn nên dựa vào.
Wladimir Palant

-1
var encodedStr = 'hello &amp; world';

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

console.log(decodedString);

@Wladimir Palant (tác giả của AdBlock Plus) đã đưa ra câu trả lời DOMParser 4 năm trước. Bạn đã đọc các câu trả lời trước khi đăng bài của bạn?
Dan Dascalescu

-7

Có một biến thể có hiệu quả 80% như câu trả lời ở đầu trang.

Xem điểm chuẩn: https://jsperf.com/decode-html12345678/1

kiểm tra hiệu suất

console.log(decodeEntities('test: &gt'));

function decodeEntities(str) {
  // this prevents any overhead from creating the object each time
  const el = decodeEntities.element || document.createElement('textarea')

  // strip script/html tags
  el.innerHTML = str
    .replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '')
    .replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, '');

  return el.value;
}

Nếu bạn cần để lại các thẻ, sau đó xóa hai .replace(...)cuộc gọi (bạn có thể để lại cuộc gọi đầu tiên nếu bạn không cần tập lệnh).


6
Xin chúc mừng, bạn đã quản lý để che giấu lỗ hổng bằng logic không có thật, tất cả vì một chiến thắng hiệu suất không thành vấn đề trong thực tế. Hãy thử gọi decodeEntities("</textarea '><img src=x onerror=alert(1) \">")trong Firefox. Vui lòng ngừng cố gắng vệ sinh mã HTML bằng các biểu thức thông thường.
Wladimir Palant
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.