Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

浏览器知识总结 #2

Open
litter-fish opened this issue Jan 9, 2022 · 0 comments
Open

浏览器知识总结 #2

litter-fish opened this issue Jan 9, 2022 · 0 comments

Comments

@litter-fish
Copy link
Owner

浏览器

渲染过程

  • 执行beforeUnload事件,卸载当前页面

  • URL 解析

    • 如果是文本,则拼接成默认搜索引擎加关键字的 URL 进行搜索

    • 如果是 URL 就进行页面访问请求,并加上协议头(http、https 的区别)

    • encodeURI 与 encodeURIComponent

      • encodeURI

        • 可以对中文空格等编码,适用于整个URL
      • encodeURIComponent

        • 范围更广,会编码一些特殊字符,如:/?=+@#$,适用于给参数进行编码
  • 判断是否存在缓存

    • 强缓存(200 from cache)与协商缓存(304)
  • DNS解析

    • 浏览器缓存中查找
    • 操作系统中查找
    • host文件中查找
    • 路由器缓存中找
    • 服务提供商中找
    • 到 DNS 服务器中查找,先根服务器,再顶级服务器
  • 建立连接

    • HTTP连接

    • HTTPS连接

      • 客户端发请求(ClientHello)
      • 服务端回应(ServerHello)
      • 客户端回应
      • 服务端最后的回应
  • 传输数据

    • 301/302

    • 根据 Content-Type 来判断响应文件类型

      • stream 类,浏览器启动下载界面下载文件。
      • text、图片类,浏览器直接展示在页面上
      • html 类型,浏览器会将响应交个渲染进程进行页面解析
  • 断开连接

    • HTTP
    • HTTPS
  • 结果渲染

    • 根据HTML构建 DOM 树

    • 样式计算(Recalculate Style)

      • 将CSS 文本转换为浏览器可以理解的结构-styleSheets(即CSSOM树)
      • 转换样式表中的属性值,使其标准化
      • 计算出 DOM 树中每个节点的具体样式(即结合DOM树和CSSOM树形成渲染树)
    • 布局

      • 创建布局树

        • 遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;而不可见的节点会被布局树忽略掉
      • 布局计算

        • 计算布局树节点的坐标位置
    • 分层

      • 思想

        • 把每个页面按照一定的规则分成多个图层,在渲染的时候只需要操作必要的图层,其他图层只需要参与合成就行
      • 什么样的 ReaderLayer 会被绘制成 GraphicsLayer

        • 具有 3D 或 transform 的CSS属性
        • filters
    • 图层绘制

      • 渲染引擎实会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表。
    • 栅格化(raster)

      • 合成线程会将图层划分为图块(tile),这些图块的大小通常是 256x256 或者 512x512
      • 合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图
    • 合成

      • 将栅格化的图块生成 DrawQuad
    • 显示

      • 输出像素点到屏幕

重绘、回流、合成

  • 重排(回流)

    • 当通过JS或者 CSS 修改元素的几何属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排

    • 重排需要更新完整的渲染流水线,所以开销也是最大的

    • 触发条件

      • 添加或者删除可见的DOM元素
      • 元素位置改变
      • 元素尺寸改变
      • 元素内容改变(例: 一个文本被另一个不同尺寸的图片替代)
      • 页面渲染初始化(无法避免)
      • 浏览器窗口尺寸改变
    • 优化方案

      • 合并多次DOM操作。比如用class来改变多个样式。
      • 避免使用table
      • 使用fragment元素(createDocumentFragment)
      • 让元素脱离文档流。即让当前元素有自己的图层
      • 多次修改时把dom 离线 ,修改完再显示。(display:none)
      • 使用采用虚拟DOM的库,如Vue,React
      • will-change: transform 启用硬件加速
  • 重绘

    • 通过JS或者 CSS 修改元素的绘制属性,例如改变元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段(即生成待绘制列表),然后执行之后的一系列子阶段,这个过程就叫重绘

    • 重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些

    • 触发条件

      • background属性(background,background-color,background-image,background-position,background-repeat,background-size)
      • outline属性(outline,outline-color,outline-style)
      • box-shadow属性
      • border属性(border-style,border-radius)
      • visibility
    • 优化方案

      • 合并多次操作
  • 合成

    • 更改一个既不要布局也不要绘制的属性,渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成

    • 比如我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作

    • 在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。

    • 触发条件

      • will-change
      • transform属性改变
      • 整个图层的几何变换,透明度变换,阴影
    • 优化方案

      • 使用will-change提前声明,使得渲染引擎将该元素单独实现一帧。(空间换时间)。

缓存

  • 缓存策略

    • 强缓存

      • 浏览器判断本地缓存未失效,直接取本地数据,不发起HTTP请求,http返回状态码是200(from memory cache或者from disk cache)

      • 请求头

        • Pragam/Expires(1.0)

          • Expires 响应头包含日期/时间, 即在此时候之后,响应过期。
        • Cache-Control(1.1)

          • max-age: 缓存资源,在指定秒数后过期
          • no-cache:相当于max-age: 0
          • no-store:请求和响应都不缓存
        • Expires和Cache-Control的区别

          • Expires 是http1.0的产物,Cache-Control是http1.1的产物
          • 两者同时存在的话,Cache-Control优先级高于Expires
          • Expires是一个具体的服务器时间
    • 协商缓存

      • 浏览器发送请求给服务端,服务端返回304,提示缓存未失效

      • 请求头

        • If-Modified-Since/Last-Modified(1.0)

          • 过程

            • 客户端第一次访问时,服务端会在响应头中加入 Last-Modified 响应头,标识这个文件在服务器端最后被修改的时间。
            • 当客户端再次访问是,会在请求头中携带一个 If-Modified-Since,用来向服务器询问该时间后文件是否被改过。
          • 缺点

            • 只能精确到秒级别
            • 无法识别文件内容是否发生变化
        • If-None-Match/Etag(1.1)

          • 过程

            • 浏览器第一次访问时,服务端返回状态码200,同时在响应头中增加Etag,存放着服务器端生成的一个序列值;
            • 浏览器第二次访问时,会在请求头中增加If-None-Match,值是第一次返回的Etag值,服务器通过比较其值判断是否过期。
          • Nginx ETag计算方式

            • 计算页面文件的最后修改时间,将文件最后修改时间的秒级Unix时间戳转为16进制作为etag的第一部分
            • 计算页面文件的大小,将大小字节数转为16进制作为etag的第二部分。
          • ETag有两种类型

            • 强ETag

              • 不论实体发生多么细微的变化都会改变其值 - "22FAA065-2664-4197-9C5E-C92EA03D0A16"
            • 弱ETag

              • 资源发生了根本改变产 生差异时才会改变 ETag - W/"22FAA065-2664-4197-9C5E-C92EA03D0A16"
  • 存储位置

    • Service Worker

      • 使用过程

        • 注册
        • 监听 install 事件,缓存文件
        • 监听 activate 事件,旧的缓存文件的清除
        • 监听 fetch 事件,拦截请求
      • 生命周期

        • installing(安装中)

          • 注册之后,触发install回调,对一些静态资源进行缓存
        • installed(安装后)

          • ServiceWorker成功安装后便进入installed状态。至此Service完成了安装过程,等待进入激活过程。
        • activating(激活中)

          • ServiceWoker安装成功后进入activating状态。
        • activated(激活后)

          • ServiceWorker可以控制页面了,可以监听功能事件了,如fetch, push事件。
        • redundant(废弃)

          • register失败
    • Memory Cache

      • 保存在内存中的缓存,tab 页关闭了就释放
    • Disk Cache

      • 保存在磁盘中
      • 会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求
    • Push Cache

      • HTTP/2 提供的缓存,session 关闭缓存就释放

      • 特性

        • Push Cache 中的缓存只能被使用一次
        • 所有的资源都能被推送,并且能够被缓存,但是 Edge 和 Safari 浏览器支持相对比较差
        • 可以给其他域名推送资源
        • 浏览器可以拒绝接受已经存在的资源推送
        • 一旦连接被关闭,Push Cache 就被释放
        • 可以推送 no-cache 和 no-store 的资源
        • 多个页面可以使用同一个HTTP/2的连接,也就可以使用同一个Push Cache
  • 用户行为对浏览器缓存的控制

    • 地址栏访问,链接跳转是正常用户行为,将会触发浏览器缓存机制
    • F5刷新,浏览器会设置max-age=0,跳过强缓存判断,会进行协商缓存判断
    • ctrl+F5刷新,跳过强缓存和协商缓存,直接从服务器拉取资源
  • 内存缓存和硬盘缓存有什么区别?

    • 内存缓存(from memory cache)

      • 快速读取

        • 将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取
      • 时效性

        • 一旦该进程关闭,则该进程的内存则会清空
      • JS,字体,图片等会放在内存缓存中

    • 硬盘缓存(from disk cache)

      • 直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢
      • CSS则会放在硬盘缓存中
  • 为什么CSS会放在硬盘缓存中?

    • CSS文件加载一次就可渲染出来,我们不会频繁读取它,所以它不适合缓存到内存中
    • js之类的脚本却随时可能会执行

浏览器存储

  • 存储方式

    • cookie
    • webStorage(localStorage和sessionStorage)
    • indexedDB
  • cookie

    • 用途

      • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
      • 个性化设置(如用户自定义设置、主题等)
      • 浏览器行为跟踪(如跟踪分析用户行为等)
    • 缺点

      • 请求头上带着数据,导致流量增加
      • 大小限制4k
      • Cookie的原生api不友好,需要自行封装
    • 参数

      • key

        • cookie的key
      • value

        • cookie的值
      • expires

        • 过期时间,当过了到期日期时,浏览器会自动删除该cookie
        • 如果不设置过期时间,则表示这个cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失
      • path

        • 路径,值可以是一个目录,或者是一个路径
        • 同目录里的所有页面,以及该目录下面任何子目录里的页面都可以访问这个cookie
      • domain

        • 同一个域下的不同主机
      • secure

        • 当secure属性设置为true时,cookie只有在https协议下才能上传到服务器
  • webStorage

    • localStorage

      • 以键值对(Key-Value)的方式存储,永久存储,永不失效,除非手动删除
      • 每个域名限制5M,打开同域的新页面也能访问得到
      • 可以存储数组、数字、对象等可以被序列化为字符串的内容
    • sessionStorage

      • 关闭页面后即被清空
  • indexedDB

    • 一个非关系型数据库,数据形式使用的是json

    • 适合存储大量数据,它的API是异步调用的

    • 特点

      • IndexedDB是一种低级API,用于客户端存储大量结构化数据。该API使用索引来实现对该数据的高性能搜索
      • 为应用创建离线版本
    • 操作方式

      • 打开数据库并且开始一个事务
      • 创建一个 object store
      • 构建一个请求来执行一些数据库操作,像增加或提取数据等
      • 通过监听正确类型的 DOM 事件以等待操作完成
      • 在操作结果上进行一些操作(可以在 request 对象中找到)

跨域

  • 协议、域名、端口只要有一个不同即是跨域。

  • 导致跨域的方式

    • DOM同源策略

      • 禁止对不同源的DOM进行操作,不同源的iframe限制互相访问。
    • XMLHttpRequest同源策略

      • 禁止使用XHR对不同源的服务器发起请求
  • 为何要有跨域限制

    • 保证用户信息的安全,防止恶意的网站窃取数据

      • AJAX同源策略用来防止CSRF攻击,如果没有AJAX同源限制,则每次发起请求都会携带源地址的cookie
  • 同源限制范围

    • Cookie、LocalStorage、IndexDB无法读取
    • DOM无法获取
    • AJAX请求不能发送
  • 没有限制的标签

    • 的src(获取图片)
    • 的href(获取css)
    • <script>的src(获取javascript)
    • iframes 允许跨域嵌入,取决于X-Frame-Options HTTP头的设置,但不允许跨域读取
    • forms 允许将数据写入跨域服务器
  • cookie

    • document.domain

      • 如果两个网址,一级域名相同,只是二级域名不同,通过设置document.domain来共享cookie和读取iframe的DOM
      • 这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 无法通过这种方法,规避同源政策,而要使用下文介绍的PostMessage API
      • 服务器也可以在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如.example.com。Set-Cookie: key=value; domain=.example.com; path=/
  • IFRAME

    • 片段标识符

      • 片段标识符(fragment identifier)指的是,URL的#号后面的部分

      • 父窗口可以把信息,写入子窗口的片段标识符。var src = originURL + '#' + data;
        document.getElementById('myIFrame').src = src;

      • 子窗口通过监听 hashchange 事件得到通知。

        window.onhashchange = checkMessage;
        function checkMessage() {
          var message = window.location.hash;
          // ...
        }
        
    • window.name

      • 浏览器窗口有window.name属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。

      • 父窗口先打开一个子窗口,载入一个不同源的网页,该网页将信息写入window.name属性。window.name = data;

      • 接着,子窗口跳回一个与主窗口同域的网址。location = 'http://parent.url.com/xxx.html';

      • 然后,主窗口就可以读取子窗口的window.name了。var data = document.getElementById('myFrame').contentWindow.name;

      • 优点

        • window.name容量很大,可以放置非常长的字符串;
      • 缺点:

        • 必须监听子窗口window.name属性的变化,影响网页性能。
    • 跨文档通信API(Cross-document messaging)

      • 新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。

      • 父窗口http://aaa.com向子窗口http://bbb.com发消息

        var popup = window.open('http://bbb.com', 'title');
        popup.postMessage('Hello World!', 'http://bbb.com');
        
      • 子窗口向父窗口发送消息

      • 父窗口和子窗口都可以通过message事件,监听对方的消息。

        window.addEventListener('message', function(e) {
          console.log(e.data);
        },false);
        
      • API

        • message事件的事件对象event,提供以下三个属性

          • event.source:发送消息的窗口
          • event.origin: 消息发向的网址
          • event.data: 消息内容
        • 可以通过event.source给发送者回复信息

          window.addEventListener('message', receiveMessage);
          function receiveMessage(event) {
            event.source.postMessage('Nice to see you!', '*');
          }
          
  • AJAX

    • WebSocket

      • WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

      • 浏览器发出的WebSocket请求的头信息

        GET /chat HTTP/1.1
        Host: server.example.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
        Sec-WebSocket-Protocol: chat, superchat
        Sec-WebSocket-Version: 13
        Origin: http://example.com
        
      • 因为有了Origin这个字段,所以WebSocket才没有实行同源政策。因为服务器可以根据这个字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应。

        HTTP/1.1 101 Switching Protocols
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
        Sec-WebSocket-Protocol: chat
        
    • JSONP

      • 原理

        • web前端事先定义一个获取跨域响应数据的回调。
        • 然后通过没有同源限制的script标签发起请求,将第1步中定义的回调函数设置在script中的src属性的query参数中
        • 服务端返回这个回调函数的执行,并将需要返回的数据放到回调函数的参数中
        • 前端的script标签请求到这个执行的回调函数后会立即执行,这样就可以拿到返回的数据了
      • 缺点

        • JSONP只能发GET请求
      • jsonp的安全性

        • CSRF攻击
        • XSS漏洞
    • CORS

      • 简单请求

        • 什么是简单请求

          • HEAD、GET、POST三种方法中的其中一种

          • Http的请求头只能包括

            • Accept
            • Accept-Language
            • Content-Language
            • Last-Event-ID
            • Content-Type(application/x-www-form-urlencoded、multipart/form-data、text/plain)
        • 基本流程

          • 浏览器在请求头中增加一个Origin属性,表明本次请求来源那个源(协议+域名+端口)

          • 服务器收到请求后根据这个值决定请求返回

            • 不允许跨域,则返回一个不带Access-Control-Allow-Origin请求头的成功响应
            • 指定许可范围内,服务器返回成功响应,同时在响应头中带上一些请求头
          • 浏览器根据上面返回的请求头中是否包含Access-Control-Allow-Origin判断是否成功

        • 相关头信息

          • Access-Control-Allow-Origin: *

          • Access-Control-Allow-Credentials: true

            • 表示浏览器是否可以发送cookie给服务器,默认情况下,Cookie不包括在CORS请求之中
          • Access-Control-Expose-Headers: Foo

            • 指定请求时XMLHTTPRequest对象的 getResponseHeader() 方法能拿到请求头
          • Content-type: text/html

      • 非简单请求

        • 对服务器有特殊要求的请求,如方法是PUT或DELETE,或者Content-Type是applcation/json

        • 基本流程

          • “预检”请求。

            • 浏览器发现CORS请求是一个非简单请求,浏览器会自动发送一个OPTIONS请求
          • 服务器对预检确认

            • 预检失败

              • 服务器返回一个不包含任何CORS相关头信息的成功请求
            • 预检成功

              • 允许跨源请求,附带一些响应头
          • 浏览器收到服务器对预检请求的确认

            • 不允许跨源请求,会触发一个错误,被 XMLHttpRequest 的onerror捕获
            • 如果通过了预检则可以正常发送请求
        • 相关头信息

          • 客户端

            • Access-Control-Request-Method

              • 列出请求用到的方法
            • Access-Control-Request-Headers

              • 一个逗号分隔的字符串,表示请求会用到请求头
          • 服务端

            • Access-Control-Allow-Methods

              • 逗号分隔的字符串,表明服务器支持的所有跨源请求,避免多次发送预检请求。
            • Access-Control-Allow-Headers

              • 逗号分隔的字符串,表明服务器支持的请求头字段
            • Access-Control-Max-Age

              • 指定本次预检的有效期,在此区间不需要发送其他预检请求

WebWorker与SharedWorker

  • 创建Worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
  • JS引擎线程与worker线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据)

面试题

  • load事件与DOMContentLoaded事件

    • 当 DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片。

      • (譬如如果有async加载的脚本就不一定完成)
    • 当 onload 事件触发时,页面上所有的DOM,样式表,脚本,图片都已经加载完成了

  • css加载是否会阻塞dom树渲染

    • css是由单独的下载线程异步下载的
    • css加载不会阻塞DOM树解析(异步加载时DOM照常构建)
    • 会阻塞render树渲染(渲染时需等css加载完毕,因为render树需要css信息)

浏览器安全

  • Web 页面安全

    • 同源策略

      • DOM 层面

        • 限制了来自不同源的 JavaScript 脚本对当前 DOM 对象读和写的操作
        • 浏览器通过引入跨文档消息机制,可以通过 window.postMessage 的 JavaScript 接口来和不同源的 DOM 进行通信
      • 数据层面

        • 限制了不同源的站点读取当前站点的 Cookie、IndexDB、LocalStorage 等数据

        • 页面可以引用第三方资源(存在XSS 的安全问题),因此又在这种开放的基础之上引入了 CSP (内容安全策略)来限制其自由程度

          • CSP 的核心思想是让服务器决定浏览器能够加载哪些资源,让服务器决定浏览器是否能够执行内联 JavaScript 代码
      • 网络层面

        • 限制了通过 XMLHttpRequest 等方式将站点的数据发送给不同源的站点
        • 跨域资源共享策略(CORS)
    • 浏览器常见攻击方式(XSS和CSRF)

      • xss(跨站脚本攻击)

        • XSS 注入的方法

          • 在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。

            <input type="text" value="<%= getParameter("keyword") %>">
            <button>搜索</button>
            <div>
              您搜索的关键词是:<%= getParameter("keyword") %>
            </div>
            
            • 当浏览器请求 http://xxx/search?keyword="><script>alert('XSS');</script> 时
            • 服务端会解析出请求参数 keyword,得到 "><script>alert('XSS');</script>
            • 拼接到 HTML 中返回给浏览器。
          • 在内联的 JS 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。

            <script>
              var initData = <%= data.toJSON() %>
            </script>
            
            • 当 JSON 中包含字符串 </script> 时,当前的 script 标签将会被闭合,后面的字符串内容浏览器会按照 HTML 进行解析
            • 通过增加下一个 <script> 标签等方法就可以完成注入
          • 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。

          • 在标签的 href、src 等属性中,包含 javascript: 等可执行代码

            <a href="<%= escapeHTML(getParameter("redirect_to")) %>">跳转...</a>
            
          • 在 onload、onerror、onclick 等事件中,注入不受控制代码。

        • 本质

          • 恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
        • XSS 攻击的分类

          • 存储型

            • 攻击者将恶意代码提交到数据库,客户端查询时拼接在HTML中返回,然后浏览器执行
          • 反射型

            • 攻击者构造恶意URL,用户打开URL后,发送请求到服务器,服务器取出内容,拼接恶意代码到HTML中返回给浏览器,浏览器执行混合恶意代码的内容
          • DOM 型

            • 攻击者构造恶意URL,用户点击后发送请求,返回后将恶意代码取出执行
            • 在 Web 资源传输过程或者在用户使用页面的过程中修改 Web 页面的数据
        • 危害

          • 窃取 Cookie 信息
          • 监听用户行为
          • 修改 DOM
        • 预防

          • 预防存储型和反射型 XSS 攻击

            • 纯前端渲染,把代码和数据分隔开

              • 浏览器先加载一个静态 HTML,此 HTML 中不包含任何跟业务相关的数据
              • JS 通过 Ajax 加载业务数据,调用 DOM API 更新到页面上
            • 对 HTML 做充分转义

              • 使用DOMPurify依赖库
              • 使用trustedTypes.createPolicy创建自定义过滤策略
          • 预防 DOM 型 XSS 攻击

            • 确保数据来源可靠
            • .innerHTML、.outerHTML、document.write(),不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent、.setAttribute() 等
            • eval()、setTimeout()、setInterval() 等,都能把字符串作为代码运行
          • 防止Cookie被盗用,使用 HttpOnly 属性

      • csrf(跨站请求伪造)

        • 攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。

        • CSRF攻击流程

          • 用户登录网址:bank.example,并保留了登录信息
          • 用户点击了攻击者提供的诱导链接,访问了benk.example
          • benk.example向bank.example发送了一个请求:withdraw?account=xm&amount=1&for=hacker。该请求会附带 bank.example 中的 cookie。
          • bank.example 接收到请求后,对请求进行验证发现携带了用户的登录信息
          • 验证成功,以攻击者的名义执行了代码
        • 类型

          • GET类型的CSRF

          • POST类型的CSRF

            <form action="http://bank.example/withdraw" method=POST>
            	  <input type="hidden" name="account" value="xiaoming" />
            	  <input type="hidden" name="amount" value="10000" />
            	  <input type="hidden" name="for" value="hacker" />
            </form>
            <script> document.forms[0].submit(); </script> 
            
            • 通常使用的是一个自动提交的表单
            • 访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作。
          • 链接类型的CSRF

        • CSRF的特点

          • CSRF(通常)发生在第三方域名
          • 攻击者利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据
          • 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”
          • 跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等
          • 目标站点一定要有 CSRF 漏洞
        • 预防

          • 阻止不明外域的访问

            • 同源检测

              • 异步请求会携带origin和referer头信息,用于表示信息来源域名,且不能进行修改,由浏览器自动附带。

              • Origin在以下两种情况下并不存在

                • IE11同源策略

                  • IE 11 不会在跨站CORS请求上添加Origin标头,Referer头将仍然是唯一的标识
                • 302重定向

                  • 在302重定向之后Origin不包含在重定向的请求中,因为Origin可能会被认为是其他来源的敏感信息。
          • 提交时要求附加本域才能获取的信息

            • Token

              • 所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击

              • 原理

                • 将CSRF Token输出到页面中

                  • 打开页面时向后台请求一个token,该token包括随机值和时间戳并经过加密算法加密,同时将该token存储在session中,页面将该token值加入到DOM树中的a标签和from标签中。
                • 页面提交的请求携带这个Token

                • 服务器验证Token是否正确

                  • 服务器取出请求的token值加密与session中的值做比较
            • 双重Cookie验证

              • 用户访问页面,向请求域名中注入一个cookie,内容为随机字符串
              • 前端向后端发送接口时,取出cookie,并添加到请求URL参数中
              • 后端校验请求URL参数和cookie中的值是否一致
          • Samesite Cookie

            • 为Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个同站 Cookie,同站Cookie只能作为第一方Cookie,不能作为第三方Cookie,Samesite 有两个属性值,分别是 Strict 和 Lax

            • Set-Cookie: foo=1; Samesite=Strict Set-Cookie: bar=2; Samesite=Lax Set-Cookie: baz=3

              • foo严格模式

                • 表明这个 Cookie 在任何情况下都不可能作为第三方 Cookie
                • 在 a.com 下发起对 b.com 的任意请求,foo 这个 Cookie 都不会被包含在 Cookie 请求头中,但 bar 会
              • bar宽松模式

                • 这个请求是这种请求(改变了当前页面或者打开了新页面)且同时是个GET请求

                  • 这个Cookie可以作为第三方Cookie
                • 当用户从 a.com 点击链接进入 b.com 时,foo 这个 Cookie 不会被包含在 Cookie 请求头中,但 bar 和 baz 会

                  • 在不同网站之间通过链接跳转Cookie不受影响
                • 从 a.com 发起的对 b.com 的异步请求,或者页面跳转是通过表单的 post 提交触发的,则bar也不会发送

  • 浏览器网络安全

    • HTTPS协议

      • 数字证书进行服务器的身份认证功能
      • 引入对称加密负责数据传输、非对称加密进行密钥协商
  • 浏览器系统安全

    • 为了提高安全性,浏览器的采用了多进程架构。并且提供了安全沙箱和站点隔离来进一步加强安全

    • 多进程架构

    • 安全沙箱

      • 利用操作系统提供的安全技术,让渲染进程在执行过程中无法访问或者修改操作系统中的数据,在渲染进程需要访问系统资源的时候,需要通过浏览器内核来实现,然后将访问的结果通过 IPC 转发给渲染进程

      • 持久存储

        • 浏览器内核负责维护一个存放所有 Cookie 的 Cookie 数据库
        • 当渲染进程通过 JavaScript 来读取 Cookie 时,渲染进程会通过 IPC 将读取 Cookie 的信息发送给浏览器内核,浏览器内核读取 Cookie 之后再将内容返回给渲染进程
        • 缓存文件的读写也是由浏览器内核实现的,比如网络文件缓存的读取
      • 网络访问

        • 渲染进程内部不能直接访问网络,需要通过浏览器内核。
        • 浏览器内核在处理 URL 请求之前,会检查渲染进程是否有权限请求该 URL
      • 用户交互

        • 为了限制渲染进程有监控到用户输入事件的能力,所以所有的键盘鼠标事件都是由浏览器内核来接收的,然后浏览器内核再通过 IPC 将这些事件发送给渲染进程
    • 站点隔离

      • Chrome 将同一站点(包含了相同根域名和相同协议的地址)中相互关联的页面放到同一个渲染进程中执行
      • 站点隔离会将不同源的 iframe 分配到不同的渲染进程中,这样即使黑客攻击恶意 iframe 的渲染进程,也不会影响到其他渲染进程的

垃圾回收机制

  • V8 内存构成

    • 新生代内存区(new space)

      • 被分成两部分,每次只使用一部分内存
    • 老生代内存区(old space)

    • 大对象区(large object space)

    • 代码区(code space)

      • 代码对象,会被分配在这里。唯一拥有执行权限的内存;
    • map 区(map space)

      • 存放 Cell 和 Map,每个区域都是存放相同大小的元素,结构简单
  • 垃圾回收技术

    • 全停顿(Stop The World)

      • 垃圾回收算法在执行前,需要将应用逻辑暂停,执行完垃圾回收后再执行应用逻辑

      • 缺点

        • 全停顿的策略导致垃圾回收中不能及时响应用户的输入,而且如果有动画会造成动画效果的卡顿
    • 增量垃圾回收(Incremental)

      • 主线程间歇性的去做少量的垃圾回收的方式

      • 优点

        • 通过 JavaScript 间歇性的执行,同时也间歇性的去做垃圾回收工作,JavaScript 的执行仍然可以在用户输入或者执行动画的时候得到及时的响应
        • 最大停顿时间减少到原来的1/6
      • 缺点

        • 没有减少主线程暂停的时间(事实上,通常会略微增加)
        • 由于写入屏障(Write-barrier )机制的成本,增量标记可能会降低应用程序的吞吐量。
    • 并行标记(Parallel)

      • 主线程和协助线程同时执行同样的工作
      • 仍然是一种 ‘stop-the-world’ 的垃圾回收方式
    • 并发标记(Concurrent)

      • 辅助线程在后台完全的执行垃圾回收
      • 主线程一直执行 JavaScript
  • Chrome当前使用的技术

    • V8 在新生代垃圾回收中使用并行标记
    • V8 中的主垃圾回收器主要使用并发标记
  • 垃圾回收的方式

    • 标记清除(mark and sweep)

      • 当变量进入执行环境(函数中声明变量,执行时)的时候,垃圾回收器将其标记为“进入环境”
      • 当变量离开环境的时候(函数执行结束)将其标记为“离开环境”
    • 引用计数

      • 跟踪记录每个值被引用的次数

      • 该方式会引起内存泄漏

        • 不能解决循环引用的问题
  • JS里的垃圾回收机制

    • 栈的垃圾回收机制

      • 通过移动ESP指针(记录当前执行状态的指针)实现垃圾回收
      • 执行栈中当一个函数执行完毕,JavaScript 引擎会通过向下移动 ESP 来销毁该函数保存在栈中的执行上下文
    • 堆的垃圾回收机制

      • 特征

        • 大部分对象在内存中存在的时间很短
        • 不死的对象,会活得更久
      • V8 把堆分为新生代和老生代

        • 存储对象

          • 生存时间短的对象
          • 生存时间久的对象
        • 内存大小

          • 32位下16MB
            64位下64MB
          • 32位下700MB
            64位下1400MB
        • 所用垃圾回收器

          • 副垃圾回收器
          • 主垃圾回收器
      • 副垃圾回收器

        • 负责新生区的垃圾回收

        • Scavenge 算法

          • 把新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域
        • 具体步骤

          • 标记

            • 对对象区域中的垃圾做标记
          • 复制

            • 把存活的对象复制到空闲区域中,同时它还会把这些对象有序地排列起来
          • 翻转

            • 对象区域与空闲区域进行角色翻转
        • 为什么新生代空间比较小

          • 复制操作需要时间成本,如果新生区空间设置得太大了,那么每次清理的时间就会过久,所以为了执行效率,一般新生区的空间会被设置得比较小
        • 怎么优化空间小的问题

          • 对象晋升策略

            • 经过两次垃圾回收依然还存活的对象,会被移动到老生区中
      • 主垃圾回收器

        • 负责老生区的垃圾回收

        • 回收方式

          • 标记 - 清除(Mark-Sweep)
          • 标记 - 整理(Mark-Compact)
        • 对象特征

          • 是对象占用空间大
          • 对象存活时间长
        • 具体步骤

          • 标记

            • 从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据

            • V8使用每个对象的两个mark-bits和一个标记工作栈来实现标记

              • 两个mark-bits编码三种颜色:白色(00),灰色(10)和黑色(11)
              • 白色表示对象可以回收
              • 黑色表示对象不能回收,并且他的所有引用都被遍历完毕
              • 灰色表示不可回收,他的引用对象没有扫描完毕
            • 具体扫描步骤

              • 从已知对象开始,即roots(全局对象和激活函数), 将所有非root对象标记置为白色
              • 将root对象的所有直接引用对象入栈(marking worklist)
              • 依次pop出对象,出栈的对象标记为黑,同时将他的直接引用对象标记为灰色并push入栈
              • 栈空的时候,仍然为白色的对象可以回收
          • 清除

            • 清除白色标记的对象
          • 标记 - 整理

            • 让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

Chrome浏览器架构

  • 优点

    • 一个页面崩溃不会影响到其他页面,因为使用了不同渲染进程
    • 沙箱处理,浏览器可以从某些功能中沙漏某些进程
  • 缺点

    • 每个进程都会包含公共基础结构的副本(例如V8,这是Chrome的JavaScript引擎),这就意味着浏览器会消耗更多的内存资源
    • 浏览器各模块之间耦合性高、扩展性差
  • 面向服务的架构

    • 各种模块会被重构成独立的服务(Service),访问服务(Service)必须使用定义好的接口,通过 IPC 来通信

    • 每个服务(Service)都可以在独立的进程中运行

    • 可以轻松拆分为不同的进程或聚合为一个进程

      • 当Chrome在功能强大的硬件上运行时,它可能会将每个服务拆分为不同的进程以提供更高的稳定性

      • 在资源受限的设备上,Chrome会将服务整合到一个进程中以节省内存。

  • Chrome的进程

    • Browser进程

      • 负责浏览器界面显示,与用户交互。如前进,后退等
      • 负责各个页面的管理,创建和销毁其他进程
      • 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
      • 网络资源的管理,下载等
    • GPU进程

      • 3D绘制
      • UI 界面采用 GPU 来绘制
    • 渲染进程

      • 将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中

      • 默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下

      • 线程

        • GUI渲染线程

          • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等
          • 重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
          • GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行
        • JS引擎线程

          • JS内核,负责处理Javascript脚本程序。(例如V8引擎)
          • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
          • 如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
        • 事件触发线程

        • 定时触发器线程

          • setInterval与setTimeout
        • 异步http请求线程

          • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
    • 网络进程

      • 负责页面的网络资源加载
    • 第三方插件进程

      • 负责插件的运行
      • 因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器 和页面造成影响
    • utility进程

      • 主进程临时需要做一些不方便的任务的情况下,可以启动一个utility进程来代替主进程执行

        • 比如图片解码、文件解压缩。如果这些“危险”的操作发生了失败,会导致整个主进程发生异常崩溃
      • 主进程与utility进程之间通过IPC消息来通信

    • UI进程

    • 存储进程

    • 设备进程

    • Audio进程

    • Video进程

    • Profile进程

  • Browser进程和浏览器内核(Renderer进程)的通信过程

    • Browser进程收到用户请求

      • 首先需要获取页面内容(譬如通过网络下载资源)
      • 随后将该任务通过RendererHost接口传递给Render进程
    • Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程,然后开始渲染

      • 渲染线程接收请求,加载网页并渲染网页,这其中可能需要Browser进程获取资源和需要GPU进程来帮助渲染
      • 当然可能会有JS线程操作DOM(这样可能会造成回流并重绘)
      • 最后Render进程将结果传递给Browser进程
    • Browser进程接收到结果并将结果绘制出来

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant