HTML实体编码
- 首先通过一个例子来说明一下HTML实体编码的重要性
// 前端 <!DOCTYPE html> <html lang=\"en\"> <meta charset=\"utf8\" /> <head> <title>测试</title> <style></style> </head> <body> <div id=\"test\"></div> <textarea name=\"\" id=\"textarea\" cols=\"30\" rows=\"10\"></textarea> <input type=\"button\" onclick=\"submit()\" value=\"提交\"> <script> function submit(){ let test = document.getElementById(\"test\"); fetch(\"http://localhost:3000\").then((res)=>{ res.text().then((response)=>{ test.innerHTML=response; }); }); } </script> </body> </html>
// node.js后端
const http = require(\'http\')
const fs = require(\'fs\')
const path = require(\'path\')
const port = 3000;
const server =http.createServer((req,res)=>{
res.statusCode = 200
let arr=[\'http://localhost:63342\',\'http://localhost:3001\']
if(arr.includes(req.headers.origin)){
res.setHeader(\'Cache-Control\',\'no-cache\')
res.setHeader(\'Connection\',\'keep-alive\')
res.setHeader(\'Access-Control-Allow-Origin\',req.headers.origin) // req.headers.origin
res.setHeader(\'Access-Control-Allow-Credentials\', \'true\')
}
res.end(\"<img src=@ οnerrοr=alert(123) />\")
})
server.listen(port,()=>{
console.log(\'start\')
})
-
然后可以发现,把后端响应的值赋值给textarea,只会在textarea显示对应的文本,并不会执行
-
然后把对应的响应内容赋值给div,就可以执行代码,弹出弹窗,不安全!
-
但是我们把后端返回的内容修改一下,改为使用HTML实体编码
res.end(\"<imgsrc=@οnerrοr=alert(123)/>\")
- 可以发现div不会执行代码,textarea也不会,都只是显示一样的文本
<img src=@ οnerrοr=alert(123) />
- 所以结论就是前端输入和输出的内容,如果需要复制给HTML标签且不经过HTML实体编码,那么会不安全
HTML实体编码介绍
-
通过上面的例子,我们可以知道,某些保留字符出现在文本节点和标签值里是不安全的
-
在XHTML中,这些保留字符出现在标签里会立刻报错,但是HTML解析器太懒了,容错性大,并不会出现语法报错
-
要想安全的使用<,&,>,\”等字符,就需要使用一套实体编码(entity encoding)的简单编码策略
-
这套HTML实体编码策略是以&符号开头,以;分号结尾的
-
在XML中只有少数几个这种编码,而在HTML中,存在数百个这种实体编码,并且常用的浏览器都支持这种用法
HTML编码有以下几种方式
- HTML实体编码,格式以&符号开头,以;分号结尾的
- HTML实体编码参考手册
<textareaname=\"\"id=\"textarea\"cols=\"30\"rows=\"10\"> <imgsrc="localhost"> </textarea>
- 结果是:
<img src=\"localhost\">
- 十进制的ASCLL编码,格式:以符号&#开头,分号;结尾
- ascll编码对照表
<textareaname=\"\"id=\"textarea\"cols=\"30\"rows=\"10\"> <imgsrc=\"localhost\"> </textarea>
- 结果是:
<img src=\"localhost\">
- Unicode字符编码,格式:以符号&#开头,分号;结尾
- 另外,下面的unicode编码参考表的数字对应的是十六进制的,但是我们需要先转换为十进制再显示!
- unicode编码参考
- 首先把0022转换为十进制是0034;003D转换为十进制是0061
<textareaname=\"\"id=\"textarea\"cols=\"30\"rows=\"10\"> <imgsrc="localhost"> </textarea>
- 结果是:
<img src=\"localhost\">
- 十六进制的ascll码,格式:以&#x开头,分号;结尾
- 参考上面的ascll表,但是注意需要先转换为十六进制。
<textareaname=\"\"id=\"textarea\"cols=\"30\"rows=\"10\"> <imgsrc="localhost"> </textarea>
- 结果是:
<img src=\"localhost\">
- 最后综合几种写法看看
<textareaname=\"\"id=\"textarea\"cols=\"30\"rows=\"10\"> <@!<> </textarea>
- 最后在textarea中显示
<@!<>
- 也就是<被解析为\”<\”;@被解析为\”@\”;!被解析为\”!\”;<被解析为\”<\”;>被解析为\”>\”
HTML实体编码使用
- 使用在标签的双引号内部
<imgsrc=\"https://wangbase.com/blogimg/asset/202107/bg2021072117.png\">
<imgsrc=\"https://wangbase.com/blogimg/asset/202107/bg2021072117.png\">
- 我们可以发现,两种写法都可以加载到图片!
- 写在标签的属性中
<imgsrc=\"https://wangbase.com/blogimg/asset/202107/bg2021072117.png\">
- 写在标签属性中,相当于破坏了标签的属性值,所以不会加载图片!
- 作为双引号,等于号
<imgsrc=\"https://wangbase.com/blogimg/asset/202107/bg2021072117.png\">
<imgsrc="https://wangbase.com/blogimg/asset/202107/bg2021072117.png\">
- 作为等于号,导致浏览器识别为属性名称未结束,错误。不会加载图片
- 作为等双引号,结果被识别为:(左边两个双引号)\”\”https://wangbase.com/blogimg/asset/202107/bg2021072117.png\”
HTML实体编码不可用于javascript
- HTML实体编码的范围是HTML文档,不包括javscript执行环境,因为javascript执行环境的解析器不是HTML解析器!
- 接下来看个代码
document.write(\'<imgsrc=@οnerrοr=alert(123)/>\')
//<imgsrc=@οnerrοr=alert(123)/>
console.log(\'<imgsrc=@οnerrοr=alert(123)/>\')
//<imgsrc=@οnerrοr=alert(123)/>
console.log(\'\\<imgsrc=@οnerrοr=alert(123)\\/\\>\')
//<imgsrc=@οnerrοr=alert(123)/>_
- 1. document.write由于最后的字符串被输出到html页面,所以还是会被html实体解码为对应标签
- 2. 由于只是在js环境中打印,所以最后没有进行html实体编码,字符串不变
- 3. 由于js的自解码机制会对纯转义字符添加反斜号,所以最后被解析为没有反斜号的形式
微信公众号:web前端进阶之路
其他参考:https://www.qqxiuzi.cn/bianma/zifushiti.php
字符实体是用一个编号写入HTML代码中来代替一个字符,在使用浏览器访问网页时会将这个编号解析还原为字符以供阅读。
这么做的目的主要有两个:
1、解决HTML代码编写中的一些问题。例如需要在网页上显示小于号(<)和大于号(>),由于它们是HTML的预留标签,可能会被误解析。这时就需要将小于号和大于号写成字符实体:
小于号这样写:< 或 <
大于号这样写:> 或 >
前面的写法称为实体名称,后面的写法则是实体编号。ISO-8859-1字符集(西欧语言)中两百多个字符设定了实体名称,而对于其它所有字符都可以用实体编号来代替。
2、网页编码采用了特定语言的编码,却需要显示来自其它语言的字符。例如,网页编码采用了西欧语言ISO-8859-1,却要在网页中显示中文,这时必须将中文字符以实体形式写入HTML代码中。
本页面字符实体编号采用Unicode标准。
参考:https://cloud.tencent.com/developer/article/1728791
