解析HTML编码的小问题

最近遇到了一个有趣的小问题,在处理文字的时候遇到了需要处理HTML编码的情况。具体来说,是例如 <>这样的玩意,需要把它们变成人类可以理解的字符。

仔细查了一下,发现这个问题虽然在网上能搜到一大堆解法,但绝大部分的解法都让人难以满意,虽然最后还是找到了较满意的解法,但是也想想说不定值得把它们列举一下当一个参考了。

大体来说,最常见的解决方法有这么几种:构造一个空div拿innerHTML,或者用一个叫he的三方库,或者用textarea代替div,或者用正则,更离谱的还有用underscore或者jQuery这种过时三方库。

相对较为现代化,让我比较满意的方法,则是使用DOMParser.parseFromString工具函数。

逐个点评一下吧。

首先StackOverFlow的这个讨论下的「最佳回答」:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var decodeEntities = (function() {
// this prevents any overhead from creating the object each time
var element = document.createElement('div');

function decodeHTMLEntities (str) {
if(str && typeof str === 'string') {
// strip script/html tags
str = str.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '');
str = str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, '');
element.innerHTML = str;
str = element.textContent;
element.textContent = '';
}

return str;
}

return decodeHTMLEntities;
})();

这个回答可以说是非常离谱了,首先,直接操作div这件事情,就像很多人讨论的,本身隐含着会被XSS注入运行脚本的可能性。然后,为了「避免」这个可能性,这个作者用了一大串正则表达式来清理输入文本里面包含的标签。

首先,正则表达式用作解析(Parsing)是一个错误的做法,这只是把XSS以一种更难处理的方式去推迟了。

其次,正则表达式本身就难以读写和维护,可能连作者自己也无法保证这个表达式能筛选出所有任意的脚本代码。

最后,我想要解决的问题只是把HTML编码给翻译成正确的符号,为什么我需要关心正则表达式和<script>标签?如果我翻译输入文本的时候,希望能输入任意的HTML,并且把它渲染成正确的代码表示,而不想执行它呢?

显然,这个代码也引入了很多争议,所以有人提出了第二个方法:使用一个叫he的三方库。

但是问题是这个库已经差不多7年没更新,而issue区的情况看起来也不让人放心。

其他人提出了使用underscore或者jQuery,但是一样的问题吧,我为什么要添加这些老旧、不再维护的库?而且像这里提到的,jQuery存在严重的安全问题。

正则表达式阵营的回答也有不少,例如这个

在看到一个比一个糟糕的回答后,终于看到了有人提到了textarea,例如这里这里

1
2
3
4
5
6
7
function decodeEntities(encodedString) {
var textArea = document.createElement('textarea');
textArea.innerHTML = encodedString;
return textArea.value;
}

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

似乎是个可以考虑的思路,因为textarea不会像div一样运行script,相对安全一点。

但是有没有办法完全不创建新的div呢?

其实之前有个问题被关闭了,关闭的问题上面有个链接指向这个问题,这里面提出了一个不依赖DOM操作也不需要三方库的方法。当然,需要较为现代的浏览器,但这应该在2025年不是问题了。

1
2
3
4
5
6
7
8
9
10
const htmlDecode = (input) => {
const 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/)'>"))
// ""

不过话说过来,如果在node后端,又该如何解决这个问题呢?

似乎还是得依赖三方库或者自己重新写一个。

作为补充阅读的话,也可以参考一下:

https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references

https://en.wikipedia.org/wiki/Character_encodings_in_HTML


解析HTML编码的小问题
http://inori.moe/2025/02/20/decoding-html-entities-using-native-javascript/
作者
inori
发布于
2025年2月20日
许可协议