写点什么

IE 安全系列:IE 浏览器的技术变迁(上)

2015 年 6 月 17 日

声明:为了更好地向读者输出更优质的内容,InfoQ 将精选来自国内外的优秀文章,经过整理审校后,发布到网站。本篇文章作者为乌云白帽子 blast,原文链接。本文是《IE 安全系列》第一篇,已由乌云网授权InfoQ 中文站转载。欢迎转发,但请保留原作者信息!

作者 blast

前言

本系列将简单介绍一下IE 的安全问题,限于篇幅和自己的认知,其中必有不足之处,欢迎大家指正。

系列中每篇文章分为三个部分,第一部分通常是背景介绍,第二部分是总结性的描述,第三部分则是详细介绍或者实践。

Internet Explorer 的历史变迁

在记忆中,从 1999 年开始接触网络,从那时跟随着 Windows 95 一起而来的 Internet Explorer 4 算起,微软已经发布了 8 个不同版本的 IE 了(如果 Spartan 算作是 Internet Explorer 12 的话)。这之中 Internet Explorer(以下简称 IE)都做了哪些变更呢?

  • IE1、IE2(1995 年):家族中最简单的“浏览器”,只支持静态的页面,现在你能用得到的许多功能它都不支持;但现在你用到的许多功能他却都有了雏形。
  • IE3(1996 年):对早期版本的改进,支持了 ActiveX 控件,支持了 JavaScript 和 VBScript(当时称为 Microsoft JScript 和 Microsoft VBScript,因为商标问题)。从这时开始支持 Web Browser 这个被人熟知的 ActiveX 控件,这保证了浏览器的可重用性。
  • IE4(1997 年):引入了 DHTML 功能,支持数据绑定,同时增强了 WebBrowser 的功能,添加了一些新特性,增加了侧边栏以及 BHO。
  • IE5(1999 年):随着 Windows 98 一起发售,支持持久会话,紧接着诞生了 XMLHttpRequest,促使了 AJAX 的发展(尽管此时 AJAX 一词都还没诞生……),引入了 HTA,还有自动填表等功能。IE5.5 版本起还支持了 128 位的加密。
  • IE6(2001 年):紧接着 Windows XP 一起发售,是给大家留下印象最深刻的浏览器,在 2002~2003 年中,IE6 的市场份额达到了 90%,IE 全家族的市场份额达到了 95%。同时,这也是最饱受诟病的一款浏览器,因为它的安全问题实在是很严重。这一版本中增加的大多是网页渲染相关的功能,例如 CSS1、DOM1 等的部分支持。
  • IE7(2006 年)、IE8(2009 年):IE6 市场份额被火狐抢走之后微软推出的新版本浏览器,这两个版本大部分都是性能调整和渲染修整以及增强。
  • IE9(2011 年),稳定版本,性能提升和 HTML5 支持,多进程支持,这使得网页假死或者崩溃时不会影响到其他页面。

IE9 之后的版本大家很容易就能搜到产品截图。

  • IE10(2011 年)/IE11(2013 年),性能有较大的增强,渲染及兼容性也得以增强,增加 DNT 支持。IE10 的性能已经和之前有较大的不同,但是顶着 IE 的名号,依然得承受着 IE6 带来的深刻影响。
  • Spartan(2015 年),代号 IE12。整合了语音助手,性能提升等。从程序上看,它确实和 IE 用的不是同一套 DLL 库。

IE 的构成

在早期的单进程 IE 中,IE 的结构大致如下:

图:以 IE6 为代表的单进程无 Tab 模式

随着多进程的引入,IE 的网页部分结构还是类似这样,但是界面宿主已经有变化了,看起来像是:

请注意上图中外壳网页分属于不同进程。

在 IE7 中,一个进程中可以运行一组网页窗口了,但是新窗口不代表在新进程里面运行。(比如你用 Ctrl+N 新建窗口,其实它还是在当前进程里面创建的),可以自己安装一个 IE7 来试验,如果没用过的话看我这个描述应该会很奇怪。开启了保护模式的进程会运行在低完整性级别下,通过一个代理进程来进行通信。

简化的进程模型如下:

下图:IE7 的进程模式

在 IE8 中,微软引入了 IE8 松散耦合进程框架 (LCIE),它使用 Jobs 来限制进程权限 (笔者试着用 Jobs 控制权限,实际应用在 IE 中的话深知不易,微软也是下了不少功夫),这个时候,开启了保护模式与未开启保护模式的 IE8 的结构类似与:

可以看到的是这个版本中 IE 的 UI Frame 和一些管理功能所在的进程运行在中完整性级别上,而保护模式下,Tab 和网页进程运行在低完整性级别中 (禁用保护模式的域下依然是中完整性级别)。

如上,HTML 和 ActiveX 控件都在网页进程里面,还有一个比较特殊的是工具栏,它也在网页进程里面。

采用这种模式的好处都有啥?首先是每个 Tab 都独立出去了,其中一个崩了也不会影响其他的 ; 至于把 UI Frame 移动到了代理进程那边,理由是加快启动速度。

而且由于采用了网页分进程的模式,所以不同完整性级别的网页、Tab 都可以归属于同一个 UI Frame,管理起来也比较方便。如果你使用过 mordenie、metroie,你会发现也许它的网页进程都是 64 位的,这是因为这个版本中不加载任何插件。即便现在许多插件都有了 64 位版本,比如 Adobe Flash Player,但是如果一味的追求 64 位化还是会导致各种插件不兼容。所以在 IE10、IE11 的 64 位版本中,浏览器的 UI Frame 以 64 位运行,而网页进程为了保证插件的兼容性,依然默认采用了 32 位进程。换句话说,即使你打开的是 64 位的 IE,但网页进程还是 32 位的。

所以,也许你会看到有 64 位的 IE 和 32 位 IE 同时存在你电脑里面的样子:

以及,启动 IE 后出现一个 64 位进程和 n 个 32 位进程的样子:

图:IE11 64bit Frame 进程和 32bit Content 进程

相对于 IE7 的模式,在 IE11 中,即使你手动执行两次 iexplore http://www.wooyun.org ,出来的也仅仅只有一个 64 位的 UI 进程。

图:IE11 的进程模式

当然,如果你开启了增强保护模式,那网页进程也会变成 64 位的。

图:IE11 启用增强保护模式

在 Windows 7 下开启这个模式,唯一的用处就是把进程变成了 64 位,但是 Windows 8 下则会引入 AppContainer 这个进程隔离模式,具体的可以参考这里

限于篇幅,与之相关的内容之后再叙述。

重要概念:什么是Markup Service?

回到IE 的核心功能上来,作为网页的渲染器,超文本标记语言HTML 想必是离不开Markup,那这个Markup 到底是什么呢? 历史上来说,Markup 其实是给演员看的,简单的说就是剧本,通常还会给它画一道蓝色的标记,标明这个东西应该谁怎么演才合适。在浏览器中,Markup 通常可以看作是一个个的标签。关于Markup Service 的内容,建议大家最开始只了解个大概即可。

图:Markup Script,当然,这个是演员用的,图像来自Google Image

图:IE 可以识别的Hyper Text Markup Langauge

例如,一个HTML 文件可能有如下内容:

复制代码
<DIV>blast<DIV>off

当浏览器解析这个文本时,浏览器会对内容做一次标准化 (我比较习惯这么称呼),之后,DOM 内容看起来像是:

复制代码
<HTML><HEAD><TITLE></TITLE></HEAD><BODY><DIV>blast<DIV>off</DIV></DIV></BODY></HTML>

这个过程你可以自己去网页 DOM 看:

图:IE11 的文档标准化

由于有元素的插入,因此这一项功能可能会引入额外的安全风险,例如我之前发的内容

或者可以说,解析器经过这一轮后,将HTML 文本转为了元素。而且为了内容的完整,有一些原来没有的元素也加进去了,例如html、head、title、body 等会自动地被解析器构造出来。

同时,解析器遇到第二个div(分块) 的时候,会自动的把第一个div 给封闭起来(怎么封闭要取决于浏览器的实现)。还有之前加入的必要(但是你没写) 的标签,比如 <html>、<body>,都会自动地被 IE 添上并封闭。

第二个需要注意的概念是 tree 和 stream(树、流) 的区别,比如:

复制代码
This<B>is</B>atest

这组“this is a test”和一对 b 标签的例子,将会被转化为如下的树。text 被当为树叶,element 被作为内节点。

复制代码
ROOT
|
+-------+--------+
| | |
"this" B "a test"
|
"is"

把文档转为 tree 之后,所有的操作都会变为类似对树的操作,例如增删子节点。提供此类操作的 API 被称为 Tree Service。

当然,自 IE4.0 之后,元素的模型操作比简单的树更强悍,比如这个例子:

复制代码
An <B>exmaple <I> of </B> elements </I> cross

B、I 的范围互相交叉,在 HTML 里面这个很常见,用树来描述则十分困难。因此,Markup Services 对这个内容不再提供类似树的操作,而是为方便控制内容暴露了一个基于流操作的模型。

图:相互交叉的范围

所以,Markup Service 的作用实际上是用来避免产生这种让人迷惑的模型层间的。

当无法用 Tree Service 时,浏览器就转而使用 Markup Service 来控制基于流操作的模型。

在基于树的模型中,网页内容被当作树的节点来处理,每个元素,或者一块 Text 都是一个节点。节点通过这种类似对树的操作方式来操作,例如从父节点中增删一个子节点。

在基于流的模型的内容操作方式中 (比如通过 Markup Service 来操作),文档的内容会通过使用类似迭代器的对象来操作。这个就像是在处理上面那个元素交叉的例子一样,这些带有部分重叠的元素通过两个 Markup Pointer 来区分,每个 Markup Pointer 指定着 Tag 从哪儿开始、到哪儿结束。所以,基于流的模型是基于树的模型的一个超集。

说了这么多,要引入我们的 Markup Pointer 了。在这之前,举一个类似的例子,C++ 中,如果要操作一个 vector,使用迭代器是非常方便的做法:

图:使用迭代器向 vector 插入一个元素

也如你所见,Markup Pointer 也有些神似迭代器。可以通过创建和操作无效文档的过程来理解一下。

注意之前“This is a test”的例子,浏览器可能不会被认为这是一个有效的 HTML 文档。

最小的有效 HTML 文档至少要有 html、head、title 和 body 四个元素,当你提供的内容中没有这些元素时,解析器会自动建立,然后把它们放到合适的位置上。

在文档解析过程中,使用 Markup Service 即可删除或者重新排列 DOM。例如,你可以整块删除 html、body 元素。你可以把 head 移动到 body 里面(但是这么做的话,文档会被当作是无效文档)。

在 IE 中,提供这个服务的类有很多,最普遍的类即 CMarkup。负责“指向元素、区域”的 Markup 指针类名字是 CMarkup Pointer,它们都派生自 CBase。

如果有关注类似的内容的话,之前发的一个 CMarkup Pointer 空指针引用的问题其实就与此相关

关于CMarkup Pointer,需要注意的地方是可能会导致IE 崩溃或者出其他错误,比如:

(1)Markup Pointer 刚创建,或者以一个无效对象为构造函数的参数创建的时候是未指向状态,也就是说啥都没指,通常这个值是 0,这个很可能会导致空指针引用 ;

(2)Markup Pointer 设置了指针粘滞 (当当前指针所在区域发生移动之后,区域内的指针是否也跟着计算新位置) 的时候,如果同时也设置重力 (重力分为左右重力,简单来说,就是在指针处插入一个内容,操作完之后,指针是应该贴着左边的内容还是右边的内容),且在经过某些操作后发生了歧义,在对 Markup Pointer 指向的部分进行移动、删除过程后,Markup Pointer 有可能会重新变成未指向状态。这是因为指针指向的内容不存在或无效了,指针已经从文档中移除 (注意是 remove) 了,但是指针自身还没有被删除 (delete),以后如果重用这个指针又没做校验的话,很可能就会出错。

(3)Markup Pointer 左右移动的时候也有可能出错。

还有就是一个经验条例,IE 中代码很依赖比较上层的有效性检查,所以一旦中底层代码接收到了无效数据,IE 就很可能会出现异常。

还是重提一下关于 Markup Service 的内容,如果之前没接触过的话,最开始只了解个大概即可,之后等了解了更多 IE 相关的内容时,这一块的东西才容易和其他部分联系起来。

参考资料

(1) 腾讯反病毒实验室: 深度解析 AppContainer 工作机制

(2) Q&A: 64- Bit Internet Explorer

(3) Windows 8 Metro/Modern Style IE 10

(4) Enhanced Memory Protections in IE10


感谢魏星对本文的策划和审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群)。

2015 年 6 月 17 日 08:042071

评论

发布
暂无评论
发现更多内容

极客大学 - 架构师训练营 第九周作业

9527

Week 8 命题作业

阿泰

企业级软件的核心价值

Marilyn

敏捷开发

数字货币合约交易所系统开发技术

薇電13242772558

区块链 数字货币

简要分析近几年商业软件开发平台的现状

Learun

企业 企业开发 企业应用

高性能IO模型:为什么单线程Redis能那么快?

小Q

Java redis 学习 架构 面试

架构师训练营 1 期 - 第八周作业(vaik)

行之

阿里P10带你深度剖析:淘宝网是如何基于Spring Cloud微服务框架搭建大型电商平台设计

Java架构追梦

Java 架构 面试 微服务 SpringCloud

函数式编程:如何高效简洁地对数据查询与变换

华为云开发者社区

编程 面向对象 数据处理

数字信封加密

莫问

接口测试文件上传(python+requests)

测试人生路

Python 接口测试

企业级软件的核心价值

Learun

敏捷开发 快速开发 企业开发 企业应用

熔断原理与实现Golang版

Kevin Wan

go microservice

charles的使用方法

Yolanda_trying

分布式集群如何实现高效的数据分布

vivo互联网技术

分布式 DHT hash 数据存储

架构师第一期作业(第8周)

Cheer

作业

简要分析近几年商业软件开发平台的现状

Marilyn

快速开发 企业开发

HTTP 前世今生

大导演

HTTP 前端进阶训练营

数据库建表、SQL、索引规范

Bruce Duan

MySQL sql 建表 规范

【涂鸦物联网足迹】涂鸦云平台数据类型和取值约束说明

IoT云工坊

人工智能 云计算 物联网 云平台 数据类型

Mock服务设计与实现:MySQL驱动字节码修改增强

华为云开发者社区

MySQL 数据库 sql

线程池 ThreadPoolExecutor 原理及源码笔记

程序员小航

Java 源码 jdk 线程池 并发

手把手教你撸一个能生成抖音风格动图的gif制作平台

徐小夕

Java css3 GitHub GIF 开源项目

飞书的「背道而驰」

ToB行业头条

自己写歌怎么编曲?4款超好用编曲软件推荐

奈奈的杂社

编曲 音频制作 midi daw

用废旧纸箱DIY智能宠物喂食器!旅行在外远程投喂“二狗子”

智能物联实验室

物联网 DIY 智能硬件

面对大促DevOps怎么做?这里有一份京东11.11 DevOps备战指南

京东智联云开发者

云计算 DevOps 运维自动化

直播预告 | 云原生在CloudQuery中的应用与实践

CloudQuery社区

数据库 sql 容器 云原生 工具软件

重大活动网络攻击面前,京东智联云的攻防之道

京东智联云开发者

云计算 网络安全 云安全

决策树算法-实战篇

比伯

Java 大数据 编程 架构 算法

usdt支付系统开发方案,币支付交易系统搭建

WX13823153201

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

IE安全系列:IE浏览器的技术变迁(上)-InfoQ