在线编辑器在处理用户输入内容时,经常需要过滤掉HTML5特有的标签,比如<article>、<video>、<canvas>等,避免非预期的内容展示或者安全风险。不同的使用场景可以选择不同的去除方式,下面分别介绍几种常用的实现方法。

在线编辑器去除HTML5标签的实用方案
一、为什么需要过滤HTML5标签?
1.1 问题从何而来
现在很多网站都有在线编辑器,比如后台发文章、用户评论、论坛发帖,甚至客服工单系统里都嵌入了富文本编辑器。这些编辑器允许用户直接输入带格式的内容,包括加粗、换行、插入图片或链接。但问题是,用户粘贴内容的时候往往会顺手带进来一大段HTML代码,里面可能包含各种HTML5标签,比如<article>、<section>、<header>、<video>、<canvas>等等。
这些标签本身并没有错,浏览器也认识它们,但放在在线编辑器的场景下就麻烦了。首先,很多网站的后台模板和样式是基于旧版HTML4或者XHTML写的,突然冒出来一个<article>标签,页面布局可能就乱了。其次,这些HTML5标签里很可能嵌套了JavaScript事件、iframe或者第三方脚本,这就成了安全漏洞的温床,攻击者可以利用它们执行恶意代码,也就是我们常说的XSS攻击。所以,无论从兼容性还是安全性的角度,都必须对这些标签做必要的过滤处理。
1.2 过滤不只是“删除标签”这么简单
可能有人会说:“直接把所有HTML标签都去掉不就行了?”但这是不行的,因为在线编辑器的价值就在于允许用户保留一些基础的排版格式,比如段落、加粗、超链接。不分青红皂白全部删掉,用户辛辛苦苦排版的内容就会变成一堆纯文本,体验非常差。所以我们要做的是“选择性过滤”,只去掉那些我们不希望出现的高风险或者不兼容的标签,而保留正常的文本和基础标签。这就是我们聊这个话题的核心:怎么精准地判断哪些标签该去掉,哪些该留下。
二、前端侧过滤HTML5标签
2.1 使用正则表达式实现过滤
前端过滤最直接的方式就是用JavaScript的世界级难题“正则表达式”去匹配和替换。这个思路很直观:我们提前列出一个HTML5标签清单,然后构建一个正则,把这些标签连同它们内部的内容全部替换成空字符串。
比如下面这段代码:
const html5Tags = ['article', 'section', 'nav', 'aside', 'header', 'footer', 'video', 'audio', 'canvas', 'figure', 'figcaption', 'details', 'summary', 'mark', 'time', 'meter', 'progress'];
const tagReg = new RegExp(`<(${html5Tags.join('|')})[^>]*>.*?</\1>|<(${html5Tags.join('|')})[^>]*\/?>`, 'gi');
const rawContent = '<article>这是文章内容</article><p>普通段落</p><video src="test.mp4"></video>';
const filteredContent = rawContent.replace(tagReg, '');
console.log(filteredContent); // 输出:<p>普通段落</p>这段代码里,正则的第一个分支<([tag])[^>]*>.*?</\1>负责匹配有开始标签和结束标签的完整块,第二个分支匹配自闭合标签。看起来好像很完美?其实问题很多。
最大的问题就是正则表达式并不懂HTML的层级关系。比如说<article>里嵌套了<p>和另一个<article>,那.*?这种非贪婪匹配可能匹配到第一个</article>就停了,剩下的内容就会残留一半。另外,如果用户在文本里写了“<article>这不是真标签”这样的字符串,正则也会把它当成标签处理。所以正则过滤适合简单的、结构明确的场景,只能作为初筛工具。
2.2 使用DOM操作精准过滤
既然正则匹配HTML这么容易出错,我们能不能让浏览器自己去解析HTML呢?当然可以。JavaScript里我们可以创建一个临时div元素,把内容放进去,浏览器就会自动把它解析成DOM树。然后我们遍历一遍所有目标标签元素,把它们里面的子节点一个个“搬”出来,再删掉标签本身,这样就实现了有选择地移除标签但保留内部内容的效果。
来看代码:
function removeHtml5Tags(content) {
const tempDiv = document.createElement('div');
tempDiv.innerHTML = content;
const html5Tags = ['article', 'section', 'nav', 'aside', 'header', 'footer', 'video', 'audio', 'canvas', 'figure', 'figcaption', 'details', 'summary', 'mark', 'time', 'meter', 'progress'];
const allNodes = tempDiv.querySelectorAll(html5Tags.join(','));
allNodes.forEach(node => {
while (node.firstChild) {
node.parentNode.insertBefore(node.firstChild, node);
}
node.parentNode.removeChild(node);
});
return tempDiv.innerHTML;
}
const testContent = '<header>头部内容</header><p>正文内容</p><canvas></canvas>';
console.log(removeHtml5Tags(testContent)); // 输出:头部内容<p>正文内容</p>这段代码的逻辑很清晰:先找到所有需要移除的标签,然后把它们内部的所有子节点(包括文本)移动到标签之前的位置,最后移除空标签。这样,<header>头部内容</header>就会变成纯文本“头部内容”,而<p>正文内容</p>被完整保留。
DOM操作的优点是很精准,不会误伤文本内容中长得像标签的字符串,因为它完全按照浏览器的解析规则来。不过它也有一点需要注意:在对动态插入的HTML内容做操作时,浏览器会执行其中的脚本吗?如果把用户输入直接赋值给innerHTML,有些浏览器会执行某些特殊标签里的脚本。因此,在使用DOM过滤前,我们应当保证这段内容已经经过了基础的安全清洗,或者是可信内容。另外,DOM操作会在页面里额外创建元素、遍历节点,对于特别大的内容段会有一定性能损耗,但正常场景下完全够用。
2.3 前端过滤的局限
说实话,前端过滤有个致命的先天缺陷:它完全依赖本地的JavaScript执行,而任何有经验的用户只要在浏览器里打开开发者工具,就可以轻易修改这些执行逻辑,重新构造请求。所以前端过滤只能用来提升正常用户的体验,不能作为唯一的安全防线。我们仍然需要把所有用户提交的数据在后端重新校验一遍。前端可以帮你首屏展示好、界面好看,但安全底线一定得靠后端守。
三、利用在线编辑器内置的过滤配置
3.1 成熟编辑器都具备白名单机制
与其自己在交付后去清洗标签,不如从一开始就不让那些HTML5标签进入编辑器。目前主流的在线编辑器,比如百度出品的UEditor、流行的TinyMCE、国产优秀编辑器wangEditor等,都提供了自定义过滤规则的接口。这个接口本质上是一个白名单:你允许哪些标签,不允许哪些标签,在编辑器初始化的时候就能设置好。
拿UEditor来说,它的配置项里有一个叫whitList的家伙,里面以对象的形式列出了每个标签及其允许的属性。比如你可以设置:
UE.getEditor('editor', {
whitList: {
p: [],
div: ['class', 'style'],
span: ['class', 'style'],
img: ['src', 'alt', 'width', 'height'],
a: ['href', 'title'],
strong: [],
em: []
}
});在这个配置里只允许上述几个标签,那么用户就算想粘贴一个<video>进来,编辑器也会自动把这段内容过滤掉,只保留里面的文字(如果配置了保留内容的话)。这种方式的好处是不用等用户输入完成后才去清洗,而是从源头切断,用户所见即所得,不会出现粘贴时内容突然消失一大块的困惑。
3.2 配置时的注意事项
在设置白名单的时候,不要只想着“不列进去就行”,还要考虑标签的属性。举个例子,图片标签<img>你可能允许,但如果允许src属性却允许了onerror事件属性,那等于把XSS漏洞迎进了门。所以配置白名单时,每个标签允许的属性也要精挑细选,对于那些事件属性(比如onclick、onload、onerror)一律不加,有特殊需求时再进行额外的转义处理。
另外,不同编辑器的配置方式大同小异,比如TinyMCE中可以用valid_elements来定义允许的标签,格式是字符串,比如'p[style],strong,em,a[href]'。无论用哪个编辑器,关键点是一致的:先在自己后台测试粘贴各种复杂的HTML内容,看看过滤效果是否达到预期,别等到上线被用户发现问题。
四、后端侧过滤HTML5标签
4.1 后端过滤为什么是必须的
不论前端做了多少过滤,后端都必须独立进行校验清洗。这不是小题大做,而是安全开发的基本常识。因为用户提交的数据可以绕过前端的任何限制,直接通过构造HTTP请求发送到服务器。如果后端没有防御,攻击者就可以轻松地提交一段恶意脚本,然后存储到数据库里,再展示给其他用户。这种存储型XSS攻击的危险程度非常高,轻则窃取用户登录态,重则控制整个网站后台。
所以,后端过滤不能偷懒。它的任务有两个:一是剔除无用的HTML5标签,保证内容兼容性;二是净化所有剩余代码,防止恶意事件属性、非法协议等危险元素混入。
4.2 PHP中的实现方式
很多老项目用PHP写后端,处理最简单的方式就是用自带的strip_tags函数。这个函数可以移除所有HTML标签,但允许你指定白名单。例如:
<?php
$allowTags = '<p><div><span><img><a><strong><em>';
$rawContent = '<article>文章内容</article><p>正常内容</p>';
$filteredContent = strip_tags($rawContent, $allowTags);
echo $filteredContent; // 输出:文章内容<p>正常内容</p>strip_tags会删除不在白名单里的标签,但保留标签内部的文本。这里就注意了:如果你的业务需求是连同标签内部的内容一起删除,比如用户插入了一段视频,你希望连视频文字说明也删掉,那strip_tags就不合适了,需要自己用正则或者其他解析器来处理。
还有一个容易被忽略的问题:strip_tags并不检查标签中的属性,也就是说它不会过滤掉<img onerror="alert(1)">中的onerror。为了安全,建议配合htmlspecialchars对属性进行转义,或者使用更专业的HTML过滤库。比如PHP社区广泛使用的HTMLPurifier,它可以根据你定义的规则进行深度过滤,不仅可以清除HTML5标签,还能移除所有危险属性,是目前PHP后端过滤的首选方案之一。
4.3 Java中的实现方式
在Java生态里,最常用的就是Jsoup这个HTML解析库。它的API设计得非常人性化,比如要移除所有<video>标签,一行代码就能搞定:
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
public class HtmlFilter {
public static String removeHtml5Tags(String content) {
Document doc = Jsoup.parse(content);
String[] html5Tags = {"article", "section", "nav", "aside", "header", "footer", "video", "audio", "canvas", "figure", "figcaption", "details", "summary", "mark", "time", "meter", "progress"};
for (String tag : html5Tags) {
doc.select(tag).remove();
}
return doc.body().html();
}
}注意,这里doc.select(tag).remove()是直接把整个标签连同里面的内容全部删除。如果你希望保留标签内部的文字,可以通过unwrap()方法来去掉外层标签但保留子节点:
for (String tag : html5Tags) {
doc.select(tag).unwrap();
}unwrap()类似于前面说的DOM操作中的“把子节点移出标签然后删除标签”,对于<header>导航内容</header>,执行后就会变成“导航内容”。
使用Jsoup做后端过滤时,我们需要明确业务需求:过滤时是移除整个标签及其内容,还是只移除标签本身保留文字。这在很多场景下是有区别的。比如<video>视频加载中</video>,删除整个标签连“视频加载中”也没了,但保留文字能让用户看到一个提示,体验会好一些。所以这个细节值得根据实际场景细细斟酌。
五、不同场景下的选择建议
5.1 纯展示场景 vs 用户输入存储场景
如果你的内容只是来自一个可信的管理员投稿,并且只是展示,不做二次编辑,那么前端过滤已经足够对付日常使用中的意外粘贴。但凡是涉及用户输入后存库、然后再展示给其他用户看的场景,就一定要前后端双重把关。前端过滤可以提升用户的操作体验,而后端过滤才是真正守护数据安全的屏障。
5.2 是否保留标签内部内容
这个需要根据产品逻辑来决定。在线编辑器里,有些标签结构是包裹性很强的,比如<details>和<summary>,它们的内容往往是用户精心填写的说明文字,如果直接删掉整个标签,那段说明就丢了。所以常见做法是“去标签,留文字”。但有些标签,比如<video>、<audio>,它们内部可能就是一些零散的文字或者兜底的提示,这时候你可以选择整个删除,避免展示出奇怪的内容。我的建议是,提前在代码里为每个标签设置不同的处理策略,而不是一刀切。
5.3 综合推荐方案
对于新项目,比较推荐的做法是“三层防线”:
第一层,在编辑器初始化时配置严格的标签白名单;第二层,在用户提交内容到后端时,用后端解析库(如Jsoup或HTMLPurifier)对内容做一次全面清洗;第三层,在展示内容给用户时,对输出结果做必要的HTML转义,尤其是属性值。三层各司其职,既能保证正常内容的展示,又能有效防止XSS攻击。
当然,如果用宝塔面板或其他集成环境,后端过滤可能要配合实际部署的PHP版本或Java容器来处理,不过原理都是一样的。
六、总结
处理在线编辑器中的HTML5标签,本质上是要平衡“可用性”和“安全性”这对矛盾。我们不能粗暴地删掉所有HTML,也不能放任危险的标签四处横飞。前端过滤可以用正则或DOM操作解决燃眉之急,编辑器内置的白名单能预防输入,后端的独立清洗才是安全底线。希望这篇文章能帮你理清思路,在自己项目的实际开发中选到合适的方案。记住,任何过滤规则都要配合足够的内容安全测试,尤其是对粘贴内容、Markdown转HTML、富文本转换这些场景多留个心眼。用最稳妥的方式保护好你的网站,也保护好的每一位访客。