HTML中特殊字符的编码规则与实践

8 次阅读

HTML中的特殊字符如果直接写入页面源码,可能被浏览器当作标签、属性或实体进行解析,从而导致显示异常,甚至产生安全问题。掌握HTML特殊字符的编码规则,不仅能够保证内容正常展示,也有助于避免XSS等常见Web安全风险。

HTML特殊字符为什么需要编码

HTML文档使用一套固定的语法结构,例如<和>用于表示标签,"和'经常用于定义属性值。如果普通文本中恰好包含这些字符,浏览器可能无法按照预期将其识别为文本。

例如:

HTML

价格 < 100 元

这里的<容易被HTML解析器视为标签起始符号。更安全、规范的写法是:

HTML

价格 < 100 元

浏览器最终显示的仍然是:

价格 < 100 元

也就是说,HTML实体编码主要解决的是“源码如何表达字符”和“浏览器最终如何显示字符”之间的转换问题。

HTML常见特殊字符编码规则

HTML中最常见的特殊字符可以通过字符实体进行表示。

原字符HTML实体十进制编码十六进制编码常见用途
&&&&实体起始符
<<<<标签起始符
>>>>标签结束符
""""双引号属性值
''''单引号属性值
空格   不换行空格
©©©©版权符号
®®®®注册商标
¥¥¥¥日元符号

其中,&尤其重要,因为它本身就是HTML字符实体的起始标记。

例如:

HTML

A & B

更规范的写法是:

HTML

A & B

浏览器最终仍然显示:

A & B

HTML字符实体的三种写法

HTML特殊字符通常有三种表达方式:命名实体、十进制数字实体和十六进制数字实体。

1. 命名实体

命名实体可读性最好,也是开发过程中最常用的形式。

HTML
<
>
&
"
 

例如:

HTML

5 < 10

Tom & Jerry

阅读HTML源码时,可以比较直观地理解这些字符的实际含义。

2. 十进制数字实体

字符也可以通过Unicode码点对应的十进制数字表示:

HTML
<
>
&

例如:

HTML

5 < 10

浏览器解析后显示:

5 < 10

3. 十六进制数字实体

十六进制表示通常以&#x开头:

HTML
<
>
&

例如:

HTML

5 < 10

同样会显示为:

5 < 10

三种方式本质上都是字符引用,实际项目中通常优先考虑可读性较好的命名实体。

<和>为什么需要特别注意

小于号<是HTML语法中非常重要的字符。

例如:

HTML
10 < 20

为了避免解析歧义,文本内容建议写成:

HTML
10 < 20

大于号>虽然在很多普通文本场景下直接使用也能够正常显示,但为了保持HTML源码的一致性和可读性,也可以使用:

HTML
>

特别是在动态生成HTML、代码高亮和展示HTML源码时,建议对<和>进行完整转义。

&是HTML编码中最容易忽略的字符

很多HTML实体都以&开头:

HTML
<
>
&
"

因此,如果文本本身需要显示一个&,就应该使用:

HTML
&

例如:

HTML

研发 & 测试

最终显示:

研发 & 测试

如果已经是一个合法实体,就不能进行无规则的重复编码,否则可能出现多次解析后的显示问题。

例如:

HTML
&

再次编码可能变成:

HTML
&amp;

此时浏览器显示的内容与预期可能不同。

HTML属性中的特殊字符编码

HTML特殊字符不仅出现在正文中,也经常出现在属性值中。

例如:

HTML
 value="Tom & Jerry">

更规范的写法是:

HTML
 value="Tom & Jerry">

如果属性值包含双引号,则尤其需要注意:

HTML
 value="He said "Hello"">

这种写法会破坏属性结构。

可以使用:

HTML
 value="He said "Hello"">

或者根据实际情况使用单引号包裹属性:

HTML
 value='He said "Hello"'>

不过,对于来自用户输入的数据,不应该简单依靠更换引号解决安全问题,而应该使用正确的HTML上下文转义机制。

HTML特殊字符编码与URL编码不是一回事

HTML实体编码、URL编码和JavaScript字符串转义经常被混淆,但它们解决的是不同问题。

例如字符:

&

在HTML中可以写成:

HTML
&

而在URL查询参数中通常需要进行URL编码:

%26

JavaScript字符串又可能涉及:

JavaScript
"Hello "World""

因此,不能看到特殊字符就统一使用某一种编码。

可以简单理解为:

场景常见编码方式
HTML文本HTML实体编码
HTML属性HTML属性上下文转义
URL参数URL百分号编码
JavaScript字符串JavaScript字符串转义
JSON数据JSON字符串规则

编码必须与最终输出环境匹配。

中文和Unicode字符是否需要编码

现代HTML文档通常采用UTF-8编码,因此中文、日文、韩文以及大量Unicode字符可以直接写入HTML。

例如:

HTML

你好,世界

只要文档字符集声明正确,就可以正常显示:

HTML
 charset="UTF-8">

对于普通中文内容,没有必要把每个字符都转换成数字实体。

例如:

HTML

你好,世界!

虽然也能够表示“你好,世界!”,但可读性明显下降。

因此,实际开发中通常建议直接使用UTF-8保存和传输Unicode文本,仅在确有必要时使用字符实体。

 不要滥用

 表示不换行空格,常见写法为:

HTML

Hello World

它与普通空格并不完全相同。

例如连续使用:

HTML
Hello   World

可以人为产生多个不可断开的空格。

但如果只是为了实现页面布局,不建议大量使用 ,更合理的方式是使用CSS:

CSS
.container {
    padding: 20px;
}

.title {
    margin-bottom: 10px;
}

HTML负责表达内容结构,CSS负责控制间距和视觉表现,这样更容易维护。

HTML实体编码与XSS安全

特殊字符编码不仅是显示问题,还与Web安全密切相关。

假设页面存在如下代码:

JavaScript
element.innerHTML = userInput;

如果userInput来自用户,并且未经处理就直接进入HTML上下文,就可能产生XSS风险。

例如攻击者提交包含HTML标签或事件处理代码的恶意内容,浏览器可能将其当作HTML解析。

更安全的处理方式取决于具体需求。如果目标只是显示普通文本,可以使用:

JavaScript
element.textContent = userInput;

而不是:

JavaScript
element.innerHTML = userInput;

如果业务确实需要将用户输入作为HTML展示,则应该采用可靠的HTML清洗方案,并根据输出上下文进行正确编码。

需要注意的是,单纯调用一个HTML实体编码函数,并不能解决所有XSS问题。因为HTML、JavaScript、CSS和URL等不同上下文具有不同的编码规则。

后端生成HTML时如何处理特殊字符

很多项目会在后端模板中输出数据库内容,例如:

HTML
${username}

如果username来自用户输入,就应该确保模板引擎默认进行HTML转义。

例如用户输入:

经过HTML文本上下文转义后,应当以普通文本形式展示,而不是让浏览器执行。

现代模板引擎通常提供自动转义能力,例如:

{{ username }}

和:

{{{ username }}}

可能代表不同的转义策略,具体规则取决于所使用的模板框架。

开发过程中不要为了“让HTML正常显示”而随意关闭自动转义。

展示HTML源码时必须进行转义

如果页面本身需要展示HTML代码,例如教程网站、在线编辑器或者代码文档,就必须将代码中的HTML标签转换成普通文本。

原始代码:

HTML
 class="box">Hello

页面源码中应该写成:

HTML
<div class="box">Hello</div>

然后浏览器页面中才能看到:

HTML
 class="box">Hello

实际项目中,如果代码量较大,可以结合代码高亮组件进行处理,而不是手动替换每个字符。

JavaScript中实现HTML特殊字符转义

前端开发中,如果确实需要将字符串安全地转换为HTML文本,可以使用DOM提供的能力。

例如:

JavaScript
function escapeHtml(value) {
    const div = document.createElement('div');
    div.textContent = value;
    return div.innerHTML;
}

const result = escapeHtml('');
console.log(result);

结果会变成类似:

HTML
<script>alert("test")</script>

如果最终目的只是把文本显示在页面上,则更推荐直接使用:

JavaScript
element.textContent = value;

这样可以避免把字符串重新作为HTML解析。

常见的HTML编码错误

错误一:把URL编码当HTML编码

错误示例:

HTML

Tom %26 Jerry

这里%26属于URL编码,在HTML文本中不会自动还原成&。

应该写成:

HTML

Tom & Jerry

错误二:HTML被重复编码

例如原本已经是:

HTML
&

又进行一次HTML转义,可能得到:

HTML
&amp;

最终用户可能看到:

&

因此,数据处理流程中要明确每个字段当前处于什么编码状态,避免重复转义。

错误三:为了布局大量使用实体空格

例如:

HTML
姓名:   张三

这种方式维护困难,也不利于响应式布局。

更推荐:

HTML
 class="user-info">
    姓名:
    张三

然后通过CSS控制间距。

错误四:为了显示HTML标签直接关闭转义

有些开发者为了让数据库中的HTML内容正常展示,直接使用未转义输出。

这种做法可能带来严重的XSS风险。除非数据来源完全可信,否则应该采用严格的HTML清洗策略。

HTML特殊字符编码实践建议

实际开发中可以遵循几个原则。

第一,统一使用UTF-8作为页面字符集:

HTML
 charset="UTF-8">

第二,普通HTML文本中的&、<等特殊字符根据HTML上下文进行转义。

第三,HTML属性值中的动态数据必须进行属性上下文转义。

第四,不要混淆HTML编码、URL编码、JavaScript转义和JSON编码。

第五,用户输入默认按照“不可信数据”处理。

第六,优先使用textContent、模板引擎自动转义等安全机制,而不是手动拼接HTML。

第七,不要为了显示效果滥用 ,页面布局优先使用CSS。

第八,避免重复编码和重复解码,建立清晰的数据处理链路。

HTML特殊字符编码速查

日常开发中最值得记住的几个实体是:

HTML
&    → &
<    → <
>    → >
"    → "
'    → '

例如一段普通文本:

Tom & Jerry < 10 > 5

转换成HTML安全文本后可以写成:

HTML
Tom & Jerry < 10 > 5

浏览器最终显示的依然是:

Tom & Jerry < 10 > 5

HTML特殊字符编码看似只是几个符号的转换,实际涉及HTML解析、字符集、模板渲染以及Web安全等多个方面。掌握实体编码的基本规则只是第一步,更重要的是根据数据最终进入的上下文选择正确的编码方式。对于动态内容,应优先依赖成熟框架提供的自动转义机制,并尽量避免直接使用innerHTML拼接不可信数据,这样才能兼顾页面显示效果、代码可维护性与安全性。