< 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编码有以下几种方式

  • HTML实体编码,格式以&符号开头,以;分号结尾的
  • HTML实体编码参考手册
  • <textarea name="" id="textarea" cols="30" rows="10">
        &lt;img src=&quot;localhost&quot;&gt;
      </textarea>
    
    <img src="localhost">
    
  • 十进制的ASCLL编码,格式:以符号&#开头,分号;结尾
  • ascll编码对照表
  • <textarea name="" id="textarea" cols="30" rows="10">
        &#60;&#105;mg src&#61;"localhost"&#62;
      </textarea>
    
    <img src="localhost">
    
  • 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">
    
  • 十六进制的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实体编码使用

  • 使用在标签的双引号内部
  •  <img src="https://wangbase.com/blogimg/asset/202107/bg2021072117.png">
        <img src="&#104;ttps://wangbase.com/blogimg/asset/202107/bg2021072117.png">
    
  • 我们可以发现,两种写法都可以加载到图片!
  • 写在标签的属性中
  •     <img sr&#99;="https://wangbase.com/blogimg/asset/202107/bg2021072117.png">
    
  • 写在标签属性中,相当于破坏了标签的属性值,所以不会加载图片!
  • 作为双引号,等于号
  •     <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前端进阶之路

    分类:
    前端
    标签: