浏览器解析机制与渲染过程

论一张图打败你学 XSS 的信心。(转自微博

附上 MDN DOM 事件参考: https://developer.mozilla.org/zh-CN/docs/Web/Events

本文参考多篇文章,涉及相关晦涩难懂名词解释,术语解释。均是 CTRL c v 。属于只可意会不可言传的领域。其他的知识都会尽量经我口中阐述的简单一点。

浏览器的解码

浏览器的解码规则

  • HTML解析器对HTML文档进行解析完成HTML解码并且创建DOM树
  • javascript 或者 CSS解析器对内联脚本进行解析,完成JS CSS解码
  • URL解码会根据URL所在的顺序不同而在JS解码前或者解码后

当浏览器从网络堆栈中获得一段内容后,触发HTML解析器来对这篇文档进行词法解析。在这一步中字符引用被解码。在词法解析完成后,DOM树就被创建好了,JavaScript解析器会介入来对内联脚本进行解析。在这一步中Unicode转义序列和Hex转义序列被解码。同时,如果浏览器遇到需要URL的上下文,URL解析器也会介入来解码URL内容。在这一步中URL解码操作被完成。由于URL位置不同,URL解析器可能会在JavaScript解析器之前或之后进行解析。

Example A: 
Example B: 
Example C: 

在例A中,HTML解析器将首先开始工作,并对UserInput中的字符引用进行解码。然后URL解析器开始对href值进行URL解码。最后,如果URL资源类型是JavaScript,那么JavaScript解析器会进行Unicode转义序列和Hex转义序列的解码。再之后,解码的脚本会被执行。因此,这里涉及三轮解码,顺序是HTML,URL和JavaScript。
在例B中,HTML解析器首先工作。然而接下来,JavaScript解析器开始解析在onclick事件处理器中的值。这是因为在onclick事件处理器中是script的上下文。当这段JavaScript被解析并被执行的时候,它执行的是“window.open()”操作,其中的参数是URL的上下文。在此时,URL解析器开始对UserInput进行URL解码并把结果回传给JavaScript引擎。因此这里一共涉及三轮解码,顺序是HTML,JavaScript和URL。
例C与例A很像,但不同的是在UserInput前多了window.open()操作。因此,对UserInput多了一次额外的URL解码操作。总的来说,四轮解码操作被完成,顺序是HTML,URL,JavaScript和URL。

HTML 解析

  1. 空元素: 空一字体现在不能容纳内容。一般的标签由 content
    这样组成。空元素意味着没有闭合标签的标签。如:


    ,

    ,
  2. 原始文本元素: 可以容纳内容。
    这个payload能执行的原因是因为
    遵循XML和SVG的定义。在XML中, (
    会被解析成
    。同理
    也可以造成 XSS

    tips: 在XML中实体会自动转义,除了 ]]>
    包含的实体
    下面开始进入HTML解析过程…

    一个HTML解析器作为一个状态机,HTML解析器有很多种状态。进行状态转换的方式是从输入流中获取字符并按照转换规则转换。以 content
    为例子。HTML识别开始和结束标签的核心是 /
    符号。当HTML解析器遇到 <
    且没有 /
    。就会进入 标签开始状态
    然后转变到 标签名状态
    前属性名状态
    … 最后进入 数据状态
    。 并释放当前标签的token。当解析器处于 数据状态
    时,它会继续解析,每当发现一个完整的标签,就会释放出一个token。

    容纳字符实体的作用: 在这些状态中HTML字符实体将会从 &#...
    形式解码。三种情况可以容纳字符实体: 数据状态中的字符引用
    RCDATA状态中的字符引用
    属性值状态中的字符引用

    有一种可以容纳字符引用的情况是 RCDATA状态中的字符引用
    。这意味着在
    或者
    。当然,这要看开始标签是哪一个。因此,在

    不弹窗, 原因为:


    不弹窗, 原因同5

    7、

    弹窗, 原因: 属性值状态中的字符引用,先进行HTML解码。然后JS执行

    8、

    不弹窗, 原因: 在JavaScript中,标识符只能包含字母或数字或下划线(“ _
    ”)或美元符号(“ $
    ”),且不能以数字开头。 onclick
    中的值会交给JS处理,在JS中只有字符串和标识符能用Unicode表示, '
    显然不行,JS执行失败。

    9、

    不弹窗, 原因: script
    标签属于原始文本元素。无法容纳字符引用,所以无法进行HTML解码。因此JS解析时并不能执行弹窗

    10、

    弹窗, 原因: 直接进入 JavaScript 解析。且发现unicode编码,其为 alert 标识符进行编码后的字符串。所以能先解码,然后执行

    11、

    不弹窗, 原因同8: 出现括号进行了unicode编码,JS无法识别编码后的控制字符

    12、

    不弹窗, 其实个人最开始看到payload是感觉能弹窗的,后来参考了别人的思路。发现 \u0031\u0032
    在解码的时候会被解码为字符串12。需要引号包裹。因此不执行JS

    13、

    不弹窗,原因同8: 出现单引号进行了unicode编码,JS无法识别编码后的控制字符

    14、

    弹窗。原因: \u000a
    在JavaScript里是换行,就是 \n
    ,直接执行
    组合拳:

    
    

    先进行HTML解码得

    
    

    然后进入 URL 模块处理,发现完整 javascript:
    协议,进行URL解码。得 javascript:\u0061\u006c\u0065\u0072\u0074(15)

    控制字符 ()
    未被unicode编码,因此进行 JavaScript 解码,成功弹窗

    浏览器渲染

    浏览器的呈现引擎

    呈现引擎默认可以解析html文档、xml文档以及图片等资源并将解析后的内容展示给用户。通过各种插件(浏览器扩展程序)浏览器还可以展示其他各类型的web资源,如pdf插件可以让浏览器展示pdf文档。不同浏览器使用的呈现引擎也不一样,目前主流的呈现引擎有Webkit、Blink(Webkit的一个分支)、Gecko、Trident、EdgeHTML(Trident的一个分支)。

    浏览器 呈现引擎
    Chrome Blink(Chrome 28+)
    Webkit(Chrome 27-)
    Safari Webkit
    Firefox Gecko
    Edge EdgeHTML
    IE Trident

    页面呈现原理

    当我们点击一个链接,服务器将 HTML 代码传输到我们的浏览器,浏览器在接收到这份 HTML 代码之后进行的页面的呈现,粗略的说会经过以下这些步骤:

    1. DOM 树的构建(Parse HTML)
    2. 构建 CSSOM 树(Recaculate Style)
    3. 合并 DOM 树与 CSSOM 树为 Render 树
    4. 布局(Layout)
    5. 绘制(Paint)
    6. 复合图层化(Composite)

    页面呈现过程中的阻塞

    1. 当遇到 JavaScript 脚本或者外部 JavaScript 代码时,浏览器便停止 DOM 的构建(阻塞 1)
    2. 当遇到