我有一些与XML-RPC后端通信的JavaScript代码。 XML-RPC返回如下形式的字符串:

<img src='myimage.jpg'>

然而,当我使用JavaScript将字符串插入到HTML中时,它们会逐字呈现。我看到的不是图像,而是字符串:

<img src='myimage.jpg'>

我猜想HTML是通过XML-RPC通道转义的。

如何在JavaScript中解除字符串转义?我尝试了这个页面上的技巧,但没有成功:http://paulschreiber.com/blog/2008/09/20/javascript-how-to-unescape-html-entities/

诊断这个问题的其他方法是什么?


当前回答

其他答案都有问题。

document.createElement('div')方法(包括使用jQuery的方法)执行传递给它的任何javascript(一个安全问题),DOMParser.parseFromString()方法修饰空白。这是一个纯javascript解决方案,没有任何问题:

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是专门用来避免执行js代码。它通过了这些:

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.

其他回答

不是对你的问题的直接回应,但它不是更好为您的RPC返回一些结构(是XML或JSON或其他)与那些图像数据(在您的例子中的url)在该结构?

然后你可以在javascript中解析它,并使用javascript本身构建<img>。

你从RPC接收到的结构可能是这样的:

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

我认为这样更好,因为将来自外部源代码的代码注入您的页面看起来不太安全。想象一下,有人劫持了您的XML-RPC脚本,并在其中放入了一些您不想要的东西(甚至是一些javascript……)

不客气只是一个信使……全部归功于ourcodeworld.com,链接如下。

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

出处:https://ourcodeworld.com/articles/read/188/encode-and-decode-html-entities-using-pure-javascript

我知道这里有很多好的答案,但由于我实现了一个有点不同的方法,我想分享一下。

这段代码是一种非常安全的安全方法,因为转义处理程序依赖于浏览器,而不是函数。因此,如果将来发现新的漏洞,将覆盖此解决方案。

const decodeHTMLEntities = text => {
    // Create a new element or use one from cache, to save some element creation overhead
    const el = decodeHTMLEntities.__cache_data_element 
             = decodeHTMLEntities.__cache_data_element 
               || document.createElement('div');
    
    const enc = text
        // Prevent any mixup of existing pattern in text
        .replace(/⪪/g, '⪪#')
        // Encode entities in special format. This will prevent native element encoder to replace any amp characters
        .replace(/&([a-z1-8]{2,31}|#x[0-9a-f]+|#\d+);/gi, '⪪$1⪫');

    // Encode any HTML tags in the text to prevent script injection
    el.textContent = enc;

    // Decode entities from special format, back to their original HTML entities format
    el.innerHTML = el.innerHTML
        .replace(/⪪([a-z1-8]{2,31}|#x[0-9a-f]+|#\d+)⪫/gi, '&$1;')
        .replace(/#⪫/g, '⪫');
   
    // Get the decoded HTML entities
    const dec = el.textContent;
    
    // Clear the element content, in order to preserve a bit of memory (it is just the text may be pretty big)
    el.textContent = '';

    return dec;
}

// Example
console.log(decodeHTMLEntities("<script>alert('&awconint;&CounterClockwiseContourIntegral;&#x02233;&#8755;⪪#x02233⪫');</script>"));
// Prints: <script>alert('∳∳∳∳⪪##x02233⪫');</script>

顺便说一下,我选择使用字符⪪和⪫,因为它们很少被使用,因此通过匹配它们影响性能的几率显著降低。

当前投票最多的答案有从字符串中删除HTML的缺点。如果这不是你想要的(这当然不是问题的一部分),那么我建议使用正则表达式来查找HTML实体(/&[^;]*;/gmi),然后遍历匹配并转换它们。

function decodeHTMLEntities(str) { if (typeof str !== 'string') { return false; } var element = document.createElement('div'); return str.replace(/&[^;]*;/gmi, entity => { entity = entity.replace(/</gm, '&lt;'); element.innerHTML = entity; return element.textContent; }); } var encoded_str = `<b>&#8593; &#67;&#65;&#78;'&#84;&nbsp;&#72;&#65;&#67;&#75;&nbsp;&#77;&#69;,&nbsp;&#66;&#82;&#79;</b>`; var decoded_str = decodeHTMLEntities(encoded_str); console.log(decoded_str);

关于XSS攻击:

虽然innerHTML不执行<script>标签中的代码,但有可能在*事件属性中运行代码,因此用户传递的字符串可能会利用上面的正则表达式:

&<img src='asdfa' error='alert(`doin\' me a hack`)' />;

因此,有必要将任何<字符转换为它们的&lt;在将它们放入隐藏的div元素之前。

此外,为了覆盖我所有的基础,我将注意到,在全局作用域中定义的函数可以通过在控制台上重新定义它们来重写,因此使用const定义这个函数或将其放在非全局作用域中非常重要。

注意:以下示例中企图利用的漏洞会使堆栈片段编辑器混淆,因为它所做的预处理,所以您必须在浏览器的控制台中运行它,或者在它自己的文件中运行它才能查看结果。

var tests = [
  "here's a spade: &spades;!",
  "&<script>alert('hackerman')</script>;",
  "&<img src='asdfa' error='alert(`doin\' me a hack`)' />;",
  "<b>&#8593; &#67;&#65;&#78;'&#84;&nbsp;&#72;&#65;&#67;&#75;&nbsp;&#77;&#69;,&nbsp;&#66;&#82;&#79;</b>"
];

var decoded = tests.map(decodeHTMLEntities).join("\n");
console.log(decoded);

结果是:

here's a spade: ♠!
&<script>alert('hackerman')</script>;
&<img src='asdfa' error='alert(`doin' me a hack`)' />;
<b>↑ CAN'T HACK ME, BRO</b>

这是我迄今为止尝试过的最全面的解决方案:

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