HTML实体编码

1,550 阅读3分钟

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=@ onerror=alert(123) />")
})

server.listen(port,()=>{
console.log('start')
})
  • 然后可以发现,把后端响应的值赋值给textarea,只会在textarea显示对应的文本,并不会执行
  • 然后把对应的响应内容赋值给div,就可以执行代码,弹出弹窗,不安全!
  • 但是我们把后端返回的内容修改一下,改为使用HTML实体编码
res.end("&lt;img src=@ onerror=alert(123) /&gt;")
  • 可以发现div不会执行代码,textarea也不会,都只是显示一样的文本
<img src=@ onerror=alert(123) />
  • 所以结论就是前端输入和输出的内容,如果需要复制给HTML标签且不经过HTML实体编码,那么会不安全

HTML实体编码介绍

  • 通过上面的例子,我们可以知道,某些保留字符出现在文本节点和标签值里是不安全的
  • 在XHTML中,这些保留字符出现在标签里会立刻报错,但是HTML解析器太懒了,容错性大,并不会出现语法报错
  • 要想安全的使用<,&,>,"等字符,就需要使用一套实体编码(entity encoding)的简单编码策略
  • 这套HTML实体编码策略是以&符号开头,以;分号结尾的
  • 在XML中只有少数几个这种编码,而在HTML中,存在数百个这种实体编码,并且常用的浏览器都支持这种用法

HTML编码有以下几种方式

  1. HTML实体编码,格式以&符号开头,以;分号结尾的
<textarea name="" id="textarea" cols="30" rows="10">
    &lt;img src=&quot;localhost&quot;&gt;
  </textarea>
  • 结果是:
<img src="localhost">
  1. 十进制的ASCLL编码,格式:以符号&#开头,分号;结尾
<textarea name="" id="textarea" cols="30" rows="10">
    &#60;&#105;mg src&#61;"localhost"&#62;
  </textarea>
  • 结果是:
<img src="localhost">
  1. Unicode字符编码,格式:以符号&#开头,分号;结尾
  • 另外,下面的unicode编码参考表的数字对应的是十六进制的,但是我们需要先转换为十进制再显示!
  • unicode编码参考
  • 首先把0022转换为十进制是0034;003D转换为十进制是0061
  <textarea name="" id="textarea" cols="30" rows="10">
    <img src&#0061;&#0034;localhost&#0034;>
  </textarea>
  • 结果是:
<img src="localhost">
  1. 十六进制的ascll码,格式:以&#x开头,分号;结尾
  • 参考上面的ascll表,但是注意需要先转换为十六进制。
<textarea name="" id="textarea" cols="30" rows="10">
    <img src&#x3D;&#x0022;localhost&#x0022;>
  </textarea>
  • 结果是:
<img src="localhost">
  • 最后综合几种写法看看
  <textarea name="" id="textarea" cols="30" rows="10">
    &lt;&#64;&#0033;&#x3c;&gt;
  </textarea>
  • 最后在textarea中显示
<@!<>
  • 也就是<被解析为"<";&#64被解析为"@";&#0033被解析为"!";&#x3c被解析为"<";>被解析为">"

HTML实体编码使用

  1. 使用在标签的双引号内部
 <img src="https://wangbase.com/blogimg/asset/202107/bg2021072117.png">
    <img src="&#104;ttps://wangbase.com/blogimg/asset/202107/bg2021072117.png">
  • 我们可以发现,两种写法都可以加载到图片!
  1. 写在标签的属性中
    <img sr&#99;="https://wangbase.com/blogimg/asset/202107/bg2021072117.png">
  • 写在标签属性中,相当于破坏了标签的属性值,所以不会加载图片!
  1. 作为双引号,等于号
    <img src&#x3D;"https://wangbase.com/blogimg/asset/202107/bg2021072117.png">
<img src=&#x22;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('&lt;img src=@ onerror=alert(123) /&gt;') // <img src=@ onerror=alert(123) />
console.log('&lt;img src=@ onerror=alert(123) /&gt;') // &lt;img src=@ onerror=alert(123) /&gt;
console.log('<img src=@ onerror=alert(123) />') // <img src=@ onerror=alert(123) />_
  • 1. document.write由于最后的字符串被输出到html页面,所以还是会被html实体解码为对应标签
  • 2. 由于只是在js环境中打印,所以最后没有进行html实体编码,字符串不变
  • 3. 由于js的自解码机制会对纯转义字符添加反斜号,所以最后被解析为没有反斜号的形式

微信公众号:web前端进阶之路