说明
浏览器工作原理与实践专栏学习笔记
单进程架构的浏览器
在最开始的阶段,浏览器是单进程的,这意味着渲染过程、JavaScript 执行过程、网络加载过程、UI 绘制过程和页面显示过程等都是在同一个进程中执行的。
从稳定性来看:
单进程架构的浏览器是不稳定的,因为只要浏览器进程中的任意一个功能出现异常都有可能影响到整个浏览器,如页面卡死、浏览器崩溃等。
从安全性来看:
如果浏览器被曝出存在漏洞,那么在这些漏洞没有被及时修复的情况下,黑客就有可能通过恶意的页面向浏览器中注入恶意程序。
最常见的攻击方式是利用缓冲区溢出,可以看看维基百科的描述。
对比一下 XSS 攻击:
- XSS 攻击只是将恶意的 JavaScript 脚本注入到页面中,虽然能窃取一些 Cookie 相关的数据,但是 XSS 无法对操作系统进行攻击。
- 而通过浏览器漏洞进行的攻击是可以入侵到浏览器进程内部的,可以读取和修改浏览器进程内部的任意内容,还可以穿透浏览器,在用户的操作系统上悄悄地安装恶意软件、监听用户键盘输入信息以及读取用户硬盘上的文件内容。
安全视角下的多进程架构
现代浏览器的设计目标是安全、快速和稳定,而浏览器被划分为浏览器内核和渲染内核两个核心模块。
浏览器内核和渲染进程
- 浏览器内核是由网络进程、浏览器主进程和 GPU 进程组成的
- 渲染内核就是渲染进程
- 浏览器内核和渲染进程之间都是通过 IPC 来通信的
浏览器内核和渲染进程的渲染流程怎么配合:
- 所有的网络资源都是通过浏览器内核来下载的
- 下载后的资源会通过 IPC 将其提交给渲染进程
- 渲染进程会对这些资源进行解析、绘制等操作,最终生成一幅图片
- 渲染进程并不负责将图片显示到界面上,而是将最终生成的图片提交给浏览器内核模块
- 最后由浏览器内核模块负责显示这张图片
那么为什么这个流程弄得这么复杂?
- 为什么一定要通过浏览器内核去请求资源,再将数据转发给渲染进程,而不直接从进程内部去请求网络资源?
- 为什么渲染进程只负责生成页面图片,生成图片还要经过 IPC 通知浏览器内核模块,然后让浏览器内核去负责展示图片?
接下来得看看安全沙箱。
安全沙箱
背景:
浏览器可以安全地下载各种网络资源,但是如果要执行这些网络资源,比如解析 HTML、解析 CSS、执行 JavaScript、图片编解码等操作,就需要非常谨慎,因为一不小心,黑客就会利用这些操作对含有漏洞的浏览器发起攻击。基于以上原因,需要在渲染进程和操作系统之间建一道墙,去阻止攻击。
概念:
安全沙箱就是将渲染进程和操作系统进行隔离的‘墙’。安全沙箱最小的保护单位是进程。因为单进程浏览器需要频繁访问或者修改操作系统的数据,所以单进程浏览器是无法被安全沙箱保护的,而现代浏览器采用的多进程架构使得安全沙箱可以发挥作用。
作用:
即便渲染进程由于存在漏洞被黑客攻击,黑客也获取不到渲染进程之外的任何操作权限。安全沙箱主要保护的是恶意程序通过网络攻破渲染进程,然后在渲染进程内,获取系统窗口的内容,比如qq登陆窗口这种。
原理:
浏览器中的安全沙箱是利用操作系统提供的安全技术,让渲染进程在执行过程中无法访问或者修改操作系统中的数据,在渲染进程需要访问系统资源的时候,需要通过浏览器内核来实现,然后将访问的结果通过 IPC 转发给渲染进程。
安全沙箱如何影响各个模块功能
浏览器内核和渲染进程各自职责:
由于渲染进程需要安全沙箱的保护,因此需要把在渲染进程内部涉及到和系统交互的功能都转移到浏览器内核中去实现。
1. 持久存储
安全沙箱是如何影响到浏览器持久存储的?
由于安全沙箱需要负责确保渲染进程无法直接访问用户的文件系统,但是在渲染进程内部有访问 Cookie 的需求、有上传文件的需求,为了解决这些文件的访问需求,所以现代浏览器将读写文件的操作全部放在了浏览器内核中实现,然后通过 IPC 将操作结果转发给渲染进程。
在浏览器内核中完成的文件内容的读写:
- 存储 Cookie 数据的读写
通常浏览器内核会维护一个存放所有 Cookie 的 Cookie 数据库,然后当渲染进程通过 JavaScript 来读取 Cookie 时,渲染进程会通过 IPC 将读取 Cookie 的信息发送给浏览器内核,浏览器内核读取 Cookie 之后再将内容返回给渲染进程。
- 一些缓存文件的读写也是由浏览器内核实现的
比如:网络文件缓存的读取。
2. 网络访问
有了安全沙箱的保护,在渲染进程内部也是不能直接访问网络的,如果要访问网络,则需要通过浏览器内核。
浏览器内核在处理 URL 请求之前,会检查渲染进程是否有权限请求该 URL,比如:检查 XMLHttpRequest 或者 Fetch 是否是跨站点请求,或者检测 HTTPS 的站点中是否包含了 HTTP 的请求。
3. 用户交互
由于每个渲染进程都有安全沙箱的保护,所以在渲染进程内部是无法直接操作窗口句柄
的,这也是为了限制渲染进程监控到用户的输入事件。
先来看看什么是窗口句柄?
窗口句柄
通常情况下,如果你要实现一个 UI 程序,操作系统会提供一个界面给你,该界面允许应用程序与用户交互,允许应用程序在该界面上进行绘制,比如:
-
Windows 提供的是
HWND
-
Linux 提供的
X Window
我们把 HWND
和 X Window
统称为窗口句柄。应用程序可以在窗口句柄上进行绘制和接收键盘鼠标消息。
实现机制
为了限制渲染进程有监控到用户输入事件的能力,所以所有的键盘鼠标事件都是由浏览器内核来接收的,然后浏览器内核再通过 IPC 将这些事件发送给渲染进程。
需要实现的就是:
- 渲染进程需要渲染出位图。为了向用户显示渲染进程渲染出来的位图,渲染进程需要将生成好的位图发送到浏览器内核,然后浏览器内核将位图复制到屏幕上。
- 操作系统没有将用户输入事件直接传递给渲染进程,而是将这些事件传递给浏览器内核。然后浏览器内核再根据当前浏览器界面的状态来判断如何调度这些事件,如果当前焦点位于浏览器地址栏中,则输入事件会在浏览器内核内部处理;如果当前焦点在页面的区域内,则浏览器内核会将输入事件转发给渲染进程。
站点隔离(Site Isolation)
所谓站点隔离:是指 Chrome 将同一站点(包含了相同根域名和相同协议的地址)中相互关联的页面放到同一个渲染进程中执行。
幽灵(Spectre)和熔毁(Meltdown)
目前所有操作系统都面临着两个 A 级漏洞——幽灵(Spectre)和熔毁(Meltdown),这两个漏洞是由处理器架构导致的,很难修补,黑客通过这两个漏洞可以直接入侵到进程的内部,如果入侵的进程没有安全沙箱的保护,那么黑客还可以发起对操作系统的攻击。
Google公司的Project Zero等安全团队披露出的英特尔等处理器芯片存在非常严重的安全漏洞,将其命名为Meltdown和Spectre。
1.幽灵(Spectre)
Spectre则是破坏了不同应用程序之间的隔离。问题的根源在于推测执行(speculative execution),这是一种优化技术,处理器会推测在未来可能执行的指令并预执行。这种技术的目的在于提前准备好计算结果,当这些数据被需要时可立即使用,以提升系统运行效率。内在的原因是CPU的运行速度大大快于内存的读取速度。在此过程中,英特尔等CPU没有很好地将低权限的应用程序与访问内核内存分开,这意味着攻击者可以使用恶意应用程序来获取应该被隔离的用户级私有数据。
2.熔毁(Meltdown)
Meltdown破坏了位于用户和操作系统之间的基本隔离,允许恶意代码访问主机任意内存,从而窃取其他应用程序以及操作系统内核的敏感信息。这个漏洞“熔化”了由硬件来实现的安全边界。允许低权限用户级别的应用程序“越界”访问系统级的内存,从而造成数据泄露。
更多可以参考:处理器A级漏洞Meltdown(熔毁)和Spectre(幽灵) FAQ
标签级的渲染进程 vs iframe 级的渲染进程
最开始 Chrome 划分渲染进程是以标签页为单位。
这样会出现一个问题:
一个标签页中可能包含了多个 iframe,而这些 iframe 又有可能来自于不同的站点,这就导致了多个不同站点中的内容通过 iframe 同时运行在同一个渲染进程中。如果一个银行站点包含了一个恶意 iframe,然后这个恶意的 iframe 利用这两个 A 级漏洞去入侵渲染进程,那么恶意程序就能读取银行站点渲染进程内的所有内容了,这对于用户来说就存在很大的风险。
Chrome 几年前就开始重构代码,将标签级的渲染进程重构为 iframe 级的渲染进程,然后严格按照同一站点的策略来分配渲染进程,这样就是可以将恶意的 iframe 隔离在恶意进程内部,使得它无法继续访问其他 iframe 进程的内容,因此也就无法攻击其他站点了。
安全沙箱能防止 XSS 和 CSRF 的攻击?
安全沙箱是不能防止 XSS 或者 CSRF 一类的攻击,安全沙箱的目的是隔离渲染进程和操作系统,让渲染进行没有访问操作系统的权利,XSS 或者 CSRF 主要是利用网络资源获取用户的信息,这和操作系统没有关系的。