HTML 实体编码 / 解码器,免费
在特殊字符和 HTML 实体之间互相转换。
常用 HTML 实体
| 字符 | 实体 | 数字 | 说明 |
|---|---|---|---|
| & | & | & | 和号 |
| < | < | < | 小于号 |
| > | > | > | 大于号 |
| " | " | " | 双引号 |
| ' | ' | ' | 单引号 |
| |   | 不换行空格 | |
| © | © | © | 版权 |
| ® | ® | ® | 注册商标 |
| ™ | ™ | ™ | 商标 |
| € | € | € | 欧元符号 |
为什么使用 HTML 实体?
<、> 和 & 等字符在 HTML 中具有特殊含义。如果直接在 HTML 中包含它们,浏览器会将其解释为代码而非内容。将这些字符编码为实体可以避免渲染问题和跨站脚本(XSS)漏洞。
常见问题
命名实体和数字实体有何不同?
命名实体使用描述性名称(&、©),而数字实体使用 Unicode 代码点(&、©)。两者显示效果相同。命名实体更易读,数字实体可用于任何 Unicode 字符。
需要对所有特殊字符进行编码吗?
至少应对 HTML 内容和属性值中的 &、<、> 和 " 进行编码。如果文档使用 UTF-8 编码,现代浏览器可以原生处理大多数其他字符。
三种形式,一个字符
HTML的「实体」(正式称为字符引用):是使用常规ASCII字符序列表示单个字符的转义序列。HTML现行标准定义了三种具体的语法形式:
- 命名形式:
&后跟WHATWG命名字符引用表中的名称,以分号结尾。示例:©表示©。 - 十进制数字形式:
&#后跟以10为基数的Unicode码位,以分号结尾。示例:©。 - 十六进制数字形式:
&#x后跟以16为基数的码位,以分号结尾。示例:©。
三种形式在渲染时可互换:©、©和©都产生可见字符©,因为它们都解析为Unicode码位U+00A9。选择哪种形式是源代码可读性的问题,与浏览器行为无关。十六进制往往与已发布的Unicode图表(使用U+XXXX表示法)相匹配,因此♥更接近官方表示法U+2665,而非♥。数字引用适用于任何Unicode字符,包括星际平面的emoji,😀渲染为😀(U+1F600 GRINNING FACE)。
实体存在的原因
三个不同的历史和实际原因:
- 转义HTML中具有语法含义的字符。解析器使用某些ASCII字符作为控制符号。
<开启标签,>关闭标签,&引入引用,而引号字符分隔属性值。如果您希望这些字符以字面文本形式出现,则必须对其转义。 - 表示文档编码中不可用的字符。在UTF-8成为Web通用标准之前(大约在2010年才超过50%),大多数HTML以US-ASCII、ISO-8859-1或Windows-1252格式提供。在这些单字节编码中,©、€、≈或α等字符根本无法用字面字节表示。写
©、€或≈是访问这些码位的唯一方式。 - 为不可见或模糊字符表明作者意图。即使在UTF-8页面上,字面的不换行空格(U+00A0)与普通空格在视觉上完全相同,写
可以让任何阅读源代码的人明白其意图。
W3C现在建议尽可能使用字面Unicode字符而非实体,「以提高无障碍性和可读性」。对于五个必需转义符,以及真正不可见或模糊的字符,实体仍然有用。
五大转义字符
当您将不可信内容插入HTML时,绝对必须转义的五个字符是<、>、&、"和'。OWASP的跨站脚本防御备忘单将这些字符列为最小必需转义集:
| 字符 | 命名 | 十进制 | 十六进制 |
|---|---|---|---|
| < | < | < | < |
| > | > | > | > |
| & | & | & | & |
| " | " | " | " |
| ' | ' / ' | ' | ' |
经验法则:每当您将不可信文本放入HTML输出时,先转义这五个字符。不这样做是绝大多数存储型和反射型XSS漏洞的根本原因。
撇号陷阱
'不是HTML 4的一部分,最初仅由XML 1.0定义并继承到XHTML 1.0。Internet Explorer在版本9(2011年发布)之前拒绝将其渲染为',并会显示字面文本'。该实体是专门为HTML5添加的,现在在每款现代浏览器中都是安全的,但为了最大限度的跨浏览器、跨规范兼容性,OWASP和大多数企业级净化库在转义单引号时仍建议输出'而非',特别是在安全关键代码中。
何时编码,何时不编码
编码决策取决于文本将出现在输出的哪个位置,而非其内容。这是HTML安全中被误解最多的一点。OWASP的指南区分了以下上下文:
- HTML元素内容:转义
< > & "(以及出于谨慎转义')。 - HTML属性值:转义相同字符加上引号字符;始终使用加引号的属性。
- JavaScript上下文:使用JavaScript转义,不是HTML转义:
\xHH或\uHHHH。 - CSS上下文:使用CSS转义:
\HH后跟一个空格。 - URL/URI参数上下文:使用百分比编码(
%HH),不是HTML编码。
每种上下文都有自己的转义规则。混用它们本身就是一个漏洞,例如,将<百分比编码为%3C并不能防止HTML元素上下文中的XSS,在那里%3C只是字面文本%3C。
避免双重编码。一个常见的错误是在数据读入系统时转义,再次存储时转义,再次读出时转义,再次渲染时也转义。结果:用户输入了5 < 10,数据库存储了5 &lt; 10,页面渲染出5 < 10而非原始内容。准则是:存储原始Unicode,仅在输出时对特定上下文进行一次编码。
HTML编码与URL编码
两种不同上下文下的两种不同转义系统,经常被混淆:
| HTML实体 | URL/百分比 | |
|---|---|---|
| 标准 | HTML Living Standard | RFC 3986 |
| 格式 | &name; 或 &#NN; | %HH(十六进制字节) |
| 上下文 | HTML标记、元素主体和属性 | URL、查询字符串、表单编码的请求体 |
| 空格 | (不换行),从不使用普通空格 | %20或+ |
| JS函数 | (解析器处理) | encodeURIComponent() / encodeURI() |
HTML属性值内的URL会叠加两种转义:先对URL非法字符进行百分比编码,然后对结果URL中的& < > "进行HTML编码。这就是为何href属性内查询字符串中的&号在HTML序列化中变为&。
简史
HTML 2.0(RFC 1866,1995年)继承了SGML的实体机制,包含约50个ISO Latin 1的命名实体。HTML 3.2(W3C,1997年1月)添加了数学和符号实体。HTML 4.01(W3C,1999年12月)最终确定了三个实体集(Latin-1、Special和Symbol),共252个命名实体,这也是旧版教程中仍能看到的「252」这一数字的来源。HTML5/WHATWG(现行标准,持续更新)吸收并大幅扩展了表格至2,000余项,主要涵盖MathML和更广泛的Unicode集。XML 1.0(1998年)定义了自己最小的五大实体集(< > & " '),该最小集就是'的起源。
更多问题
在现代代码中,我实际应该使用什么?
生产代码通常不手工编写实体编码,而是调用库。客户端HTML净化使用DOMPurify。Python标准库中的html.escape()。PHP中的htmlspecialchars()。Go中的html/template(默认自动转义)。Java使用OWASP Java Encoder。在React中,写<div>{userInput}</div>会自动转义;名为dangerouslySetInnerHTML的逃生舱口被刻意命名以阻止随意使用。像本页面这样的独立编码器用作健全性检查/调试工具,而非这些库的替代品。
关于电子邮件模板中的标签怎么样?
较旧的电子邮件客户端(尤其是Outlook)将未编码的&解释为格式错误的属性,并可能剥离周围的标记。HTML电子邮件开发者学会了防御性地对每个特殊字符进行实体编码。同样适用于在存储前重写内容的论坛BBCode风格系统;往返过程可能引入意外的字面实体。
JavaScript中的textContent与innerHTML有什么区别?
在原生JavaScript中防止XSS的最重要规则:使用element.textContent = userInput而非element.innerHTML = userInput。设置textContent会将字符串写为字面文本,浏览器在内部处理所有转义。设置innerHTML会将字符串解析为HTML,执行其中包含的任何<script>标签或事件处理属性。如果确实需要标记,请先使用DOMPurify等库进行净化。
编码器能处理emoji吗?
可以,通过数字引用。emoji没有命名实体,它们都使用数字形式。😀渲染为😀,❤️渲染为红心❤️(心形码位加emoji呈现选择器)。浏览器在内部处理隐式UTF-16代理对转换;您不应手动写代理半项。
有任何内容会发送到服务器吗?
不会。编码和解码是纯函数字符串变换,完全通过JavaScript在浏览器中运行。您的任何输入都不会被上传;页面加载后可离线工作。这一点很重要,因为往返传输测试载荷的云端编码器本身如果托管站点遭到入侵,可能成为XSS攻击向量。