我有一些与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/
诊断这个问题的其他方法是什么?
你需要解码所有编码的HTML实体或只是&本身?
如果你只需要处理&然后你可以这样做:
var decoded = encoded.replace(/&/g, '&');
如果你需要解码所有HTML实体,那么你可以不使用jQuery:
var elem = document.createElement('textarea');
elem.innerHTML = encoded;
var decoded = elem.value;
请注意下面Mark的评论,他强调了这个答案早期版本中的安全漏洞,并建议使用textarea而不是div来减轻潜在的XSS漏洞。无论使用jQuery还是纯JavaScript,这些漏洞都存在。
CMS的答案很好,除非你想要取消转义的HTML非常长,超过65536个字符。因为在Chrome中,内部HTML被分割成许多子节点,每个子节点最长65536个,你需要将它们连接起来。这个函数也适用于很长的字符串:
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;
}
有关innerHTML最大长度的更多信息,请参阅以下答案:https://stackoverflow.com/a/27545633/694469
我尝试从JSON数组中删除&。上面的例子都不是,但是https://stackoverflow.com/users/2030321/chris提供了一个很好的解决方案,让我解决了我的问题。
var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText
我没有使用,因为我不知道如何将它插入一个模态窗口,将JSON数据拉到一个数组中,但我确实尝试了基于示例的这一点,并且它工作:
var modal = document.getElementById('demodal');
$('#ampersandcontent').text(replaceAll(data[0],"&", "&"));
我喜欢它,因为它简单,而且有效,但不确定为什么它没有被广泛使用。搜索hi & low找到一个简单的解决方案。
我继续寻求对语法的理解,以及使用它是否有任何风险。还什么都没找到。
var htmlEnDeCode = (function() {
var charToEntityRegex,
entityToCharRegex,
charToEntity,
entityToChar;
function resetCharacterEntities() {
charToEntity = {};
entityToChar = {};
// add the default set
addCharacterEntities({
'&' : '&',
'>' : '>',
'<' : '<',
'"' : '"',
''' : "'"
});
}
function addCharacterEntities(newEntities) {
var charKeys = [],
entityKeys = [],
key, echar;
for (key in newEntities) {
echar = newEntities[key];
entityToChar[key] = echar;
charToEntity[echar] = key;
charKeys.push(echar);
entityKeys.push(key);
}
charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
}
function htmlEncode(value){
var htmlEncodeReplaceFn = function(match, capture) {
return charToEntity[capture];
};
return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
}
function htmlDecode(value) {
var htmlDecodeReplaceFn = function(match, capture) {
return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
};
return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
}
resetCharacterEntities();
return {
htmlEncode: htmlEncode,
htmlDecode: htmlDecode
};
})();
这是ExtJS的源代码。
其他答案都有问题。
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('<& >'); // 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.
我知道这里有很多好的答案,但由于我实现了一个有点不同的方法,我想分享一下。
这段代码是一种非常安全的安全方法,因为转义处理程序依赖于浏览器,而不是函数。因此,如果将来发现新的漏洞,将覆盖此解决方案。
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('∳∳∳∳⪪#x02233⪫');</script>"));
// Prints: <script>alert('∳∳∳∳⪪##x02233⪫');</script>
顺便说一下,我选择使用字符⪪和⪫,因为它们很少被使用,因此通过匹配它们影响性能的几率显著降低。