function
submit
(
){
let
test =
document
.
getElementById
(
"test"
);
fetch
(
"http://localhost:3000"
).
then
(
(
res
)=>
{
res.
text
().
then
(
(
response
)=>
{
test.
innerHTML
=response;
</
script
>
</
body
>
</
html
>
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)
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("<img src=@ onerror=alert(123) />")
可以发现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">
<img src="localhost">
</textarea>
<img src="localhost">
十进制的ASCLL编码,格式:以符号&#开头,分号;结尾
ascll编码对照表
<textarea name="" id="textarea" cols="30" rows="10">
<img src="localhost">
</textarea>
<img src="localhost">
Unicode字符编码,格式:以符号&#开头,分号;结尾
另外,下面的unicode编码参考表的数字对应的是十六进制的,但是我们需要先转换为十进制再显示!
unicode编码参考
首先把0022转换为十进制是0034;003D转换为十进制是0061
<textarea name="" id="textarea" cols="30" rows="10">
<img src&
</textarea>
<img src="localhost">
十六进制的ascll码,格式:以&#x开头,分号;结尾
参考上面的ascll表,但是注意需要先转换为十六进制。
<textarea name="" id="textarea" cols="30" rows="10">
<img src&
</textarea>
<img src="localhost">
最后综合几种写法看看
<textarea name="" id="textarea" cols="30" rows="10">
<@!<>
</textarea>
最后在textarea中显示
也就是<被解析为"<";@被解析为"@";!被解析为"!";<被解析为"<";>被解析为">"
HTML实体编码使用
使用在标签的双引号内部
<img src="https://wangbase.com/blogimg/asset/202107/bg2021072117.png">
<img src="https://wangbase.com/blogimg/asset/202107/bg2021072117.png">
我们可以发现,两种写法都可以加载到图片!
写在标签的属性中
<img src="https:
写在标签属性中,相当于破坏了标签的属性值,所以不会加载图片!
作为双引号,等于号
<img src="https:
<img src="https:
作为等于号,导致浏览器识别为属性名称未结束,错误。不会加载图片
作为等双引号,结果被识别为:(左边两个双引号
) ""https://wangbase.com/blogimg/asset/202107/bg2021072117.png"
HTML实体编码不可用于javascript
HTML实体编码的范围是HTML文档,不包括javscript执行环境,因为javascript执行环境的解析器不是HTML解析器!
接下来看个代码
document.write('<img src=@ onerror=alert(123) />') // <img src=@ onerror=alert(123) />
console.log('<img src=@ onerror=alert(123) />') // <img src=@ onerror=alert(123) />
console.log('<img src=@ onerror=alert(123) />') // <img src=@ onerror=alert(123) />_
1. document.write由于最后的字符串被输出到html页面,所以还是会被html实体解码为对应标签
2. 由于只是在js环境中打印,所以最后没有进行html实体编码,字符串不变
3. 由于js的自解码机制会对纯转义字符添加反斜号,所以最后被解析为没有反斜号的形式
微信公众号:web前端进阶之路