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="http://www.example.com">
中,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 style="xss:expression(alert(/xss/))" />
,自己测试失败!
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>document.getElementById("1").innerHTML = "<img src=# onu0065rror=alert(1)>";</script>
这里的解析过程就不一样的,过程为:
1、HTML解析器构造DOM树p标签
2、然后碰到<script>
标签了,于是HTML解析器就会停下来,让js解析器开始,进行脚本的执行,遇到onu0065rror
先会进行解码为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>document.getElementById("1").innerHTML = "<img src=# onu0065rror=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)//
可以进行绕过
参考文章:http://taligarsiel.com/Projects/howbrowserswork1.htm
参考文章:http://xuelinf.github.io/2016/05/18/编码与解码-浏览器做了什么/