前言
本文目标
-
通过从输入url到页面显示这一整个运行过程,梳理以下相关知识点
http1.1
,http2.0
,https
,TCP/UDP
,网络架构
,缓存
浏览器进程&线程
,渲染机制
,跨域
,安全(XSS,CSRF)
-
主要是把知识成体系的建立起来
依赖知识
-
线程与进程
-
并发与并行
-
同步与异步
-
阻塞与非阻塞
-
浏览器的进程和线程
主体大纲
-
用户输入 (
输入解析
) -
检查缓存(
强缓存和协商缓存
) -
DNS解析(
IP反查
,DNS缓存
) -
建立TCP连接(
三次握手
,UDP
,网络架构
,等待tcp队列
) -
建立TLS连接 (
HTTPS
) -
发起http请求(
http1.0
,http1.1
,http2.0
,请求行
,请求头
,请求体
) -
服务器处理请求
-
服务器响应请求(
响应行
,响应头
,响应体
,状态码
) -
断开TCP连接(
四次分手
) -
渲染开始(
从进程的角度讲
,中间有一个导航流程
,) -
构建dom树
-
样式解析计算(
css阻塞问题
,结构化,属性标准化,元素样式具体化(继承,层叠,特指)
) -
创建布局树(
创建可见元素的布局树
,计算节点位置
,优化(重绘,重排相关)
) -
分层,生成分层树(
原因
,分层条件
) -
生成绘制列表,交给合成线程
-
合成线程将图层分为图块,光删化将图块变成位图(
分图块原因
) -
合成线程发送绘制图块命令,给浏览器进程
-
浏览器生成页面,显示到显示器上
依赖知识
1. 线程与进程
-
进程
- 它是程序的一次执行过程,就是一个程序的运行实例,程序执行过程中分配和管理资源的基本单位,
- 详细的说,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程
-
线程
- 是CPU调度和分派的基本单位,和同一进程的其他线程共享资源
-
关系
- 线程是进程的一部分,不能单独存在的,它是由进程来启动和管理的,一个进程有多个线程
2. 并发和并行
- 并发的关键是你有处理多个任务的能力,不一定要同时。(吃饭吃一半电话来了,去打电话,再吃饭)
- 并行的关键是你有同时处理多个任务的能力(一边打电话,一边吃饭,)
关键区别是否同时同时就是并行
3. 同步和异步
同步和异步关注的是消息通信机制
- 同步
- 在发出一个调用时,在没有得到结果之前,该调用就不返回
- 由调用者主动等待这个调用的结果
- 异步
- 一个异步过程调用发出后,调用者不会立刻得到结果
- 而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用
关键区别 主动等,还是被通知
4. 阻塞和非阻塞
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
- 阻塞
- 调用结果返回之前,当前线程会被挂起
- 调用线程只有在得到结果之后才会返回,不能做别的事
- 非阻塞
- 在不能立刻得到结果之前,该调用不会阻塞当前线程
- 当前线程可以做别的事
关键区别结果返回前,可不可以做别的
5. 浏览器线程与进程
从输入url到页面显示这一整个运行过程,浏览器进程和线程是必不可少的角色,先来了解下他们
浏览器线程与进程关系图如下
浏览器的多进程
- 浏览器主进程:界面显示,用户交互,子进程管理,存储
- 插件进程:每种类型插件对应一个进程
- GPU进程: UI的绘制,包括3D图像
- 渲染进程:每个tab一个进程
- 网络进程: 加载页面网络资源
渲染进程的多线程
-
GUI渲染线程:
- 负责渲染浏览器界面,解析html, css
- 与JS引擎线程互斥,js引擎执行时,GUI线程会被挂起, 会造成页面阻塞
-
JS引擎线程
- 解析JS脚本,运行代码
- 与GUI渲染线程互斥
-
事件触发线程
-
是属于浏览器,用来控制事件循环,
-
当js执行如setTImeout,等异步代码,会将染污添加到事件线程中
-
对应事件符合条件被触发时改线程会把事件添加到待处理队列的队尾,等待js引擎的处理
-
js是单线程的关系,待处理队列中的事件都得排队等待js引擎处理
-
js引擎线程只负责执行看病,事件触发线程帮他挂号,排队
-
定时触发器线程
-
就是setINterval,setTimeout所在线程
-
浏览器的定时计数器,并不是由js引擎计数的,(js引擎单线程,处于阻塞状态会影响计时的准确性)
-
通过单独线程计时,并触发定时(计时完毕后,添加到事件队列,等待js引擎空闲后执行)
-
w3c规定,setTimeout 低于4ms 时间间隔的都算为4ms
-
定时触发器线程,帮助计时
-
异步http请求线程
-
XMR连接后,浏览器新开一个线程请求
-
检测到状态变更,设有回调函数,异步线程产生状态变更事件,就将回调再放入事件队列中,等待js引擎执行
-
在浏览器中打开一个页面,会开启几个进程
- 一般情况是4个
- 1个浏览器进程、1个网络进程、一个GPU进程,通常一个Tab页对应一个渲染进程,
但有其它情况
-
如果页面中有iframe的话,iframe也会运行在单独的进程中
-
如果页面有插件,插件也需要开启一个单独的进程
-
如果浏览器中装了扩展,一个扩展对应一个进程
-
如果两个页面属于同一站点,并且从a页面中打开b页面,那么他们会公用一个渲染进程
一. 用户输入
- 用户输入URL,
浏览器进程
会根据URL规则进行判定解析,
- 如果是搜索词,则使用浏览器设置的默认搜索引擎,
- 如果是URL,则会根据规则把URL补全,比如输入
baidu.com
则补充协议https://www.baidu.com/
浏览器进程
会通过进程间通信(IPC)把 URL 请求发送至网络进程
,网络进程
接收到 URL 请求后,发起URL请求流程, 构建请求行信息,
二. 检查缓存
网络进程
会查找本地缓存是否缓存了该资源。如果有缓存资源且缓存有效,那么直接返回资源给浏览器进程
;如果在缓存中没有查找到资源,那么直接进入网络请求流程
那么如果才会有缓存且判定缓存有效呢
强缓存和协商缓存
缓存分为强缓存和协商缓存
1. 强缓存
- Expries
- http1.0 服务端返回的数据到期时间
- 缺点:客户端和服务器端时间有误差
- cache-control
- http1.1 多个属性
- private:客户端可以缓存
- public:客户端和代理服务器都可以缓存
- max-age=t:缓存内容将在t秒后失效
- no-cache:需要使用协商缓存来验证缓存数据
- no-store:所有内容都不会缓存
2. 协商缓存
- Last-Modified
-
http1.0 资源最后修改时间(服务器在响应请求时,会告诉浏览器资源的最后修改时间)
-
缺点:
【1】资源被修改,时间变了,但实际内容没变
【2】短时间频繁修改,秒以下级别无法捕捉
-
if-Modified-Since: 再次请求时会利用此属性携带Last-Modified时间,用于服务资源比较
-
if-Unmodified-Since: 从某个时间点算起, 是否文件没有被修改,使用的是相对时间,不需要关心客户端和服务端的时间偏差,利用它实现断点续传功能的判断
-
- Etag
- http1.1 服务器生成的唯一标识
- If-None-Match 再次请求时会利用此属性携带缓Etag标识,用于服务资源比较
- If-Match 资源比较匹配了才会返回内容,不匹配则416(请求无法满足)
3. 优先级
优先级
强缓存 > 协商缓存 高版本 > 低版本
三. DNS解析
- 进入网络请求流程,首先要进行DNS解析,也就是IP反查,获取IP地址,
域名缓存
- DNS也是有缓存的,优先级由高到低
- 浏览器缓存
- 系统缓存
- 路由缓存
- host
- 域名服务器
四. 建立TCP连接
- 拿到服务器 IP 地址,和服务器建立
TCP
链接,
网络架构 四层?五层?七层?
OSI七层 | 网络五层 | TCP/IP四层 |
---|---|---|
应用层 | 应用层 | 应用层 |
表示层 | ||
会话层 | ||
传输层 | 传输层 | 传输层 |
网络层 | 网络层 | 网络层 |
数据链路层 | 数据链路层 | 数据链路层 |
物理层 | 物理层 | - |
-
应用层
HTTP
生成http请求报文数据包发送
-
传输层
TCP/UDP
报文分割,打上标记及端口号数据包送达具体应用
- 【UDP】协议传输速度快,但是不提供重发机制,不保证数据可靠性, 适用于关注速度,不严格要求数据的领域,视频,互动游戏等
- 【TCP】协议 传输速度慢,提供重新传输机制,提供数据包重排机制,保证数据可靠性
-
网络层
IP
增加作为通信目的地的mac地址数据包送达主机
三次握手
SYN
是同步,ACK
是应答seq
是同步序列号,ack
是应答号,- 两端会使用 ISN产生器,产生各自的 初始序列号 (Initial Sequence Number, ISN), 通常两者并不相等
- 第一次握手:客户端给服务端发一个
SYN
报文,并指明客户端的初始化序列号ISN(c)
。此时客户端处于SYN_SEND
状态。 - 第二次握手:服务器收到客户端的
SYN
报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号ISN(s)
,同时会把客户端的ISN + 1
作为ACK
的值,表示自己已经收到了客户端的 SYN,此时服务器处于SYN_REVD
的状态。 - 第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于
establised
状态。 - 服务器收到 ACK 报文之后,也处于
establised
状态,此时,双方以建立起了链接。
'Client' 'Server'
【1】SYN(seq=ISN(c))
`SYN_SEND` --------------------->
同步已发送
【2】SYN + ACK(seq=ISN(s),ack=ISN(c)+1)
`establised` <--------------------- `SYN_REVD`
已连接 同步接收
【3】ACK(ack=ISN(s)+1)
---------------------> `establised`
已连接
为什么三次
- 需要三次握手双方才能确认自己和对方的接收与发送能力是否正常
TCP和UDP
- UDP协议
- 传输速度快,但是不提供重发机制,不保证数据可靠性,
- 适用于关注速度,不严格要求数据的领域,视频,互动游戏等
- TCP协议
- 传输速度慢,提供重新传输机制,提供数据包重排机制,保证数据可靠性
TCP队列等待
- 同一个域名同时最多只能建立 6 个 TCP 连接,多余6个请求同时发生,那么剩下的请求会进入排队等待状态
- http1.1一个tcp同时只能处理一个请求,浏览器会为每个域名维护6个tcp连接,但是每个tcp连接是可以复用的,也就是处理完一个请求之后,不断开这个tcp连接,可以用来处理下个http请求
- http2是可以并行请求资源的,浏览器只会为每个域名维护一个tcp连接
五. 建立TLS连接
-
如果是
HTTPS
请求还要建立TLS
链接HTTPS链接和HTTP链接都建立在TCP协议之上
HTTP请求 = TCP握手
HTTPS请求 = TCP握手 + TLS握手
https
-
https简单说就是对数据传输加密,保证安全
-
HTTP + 加密 + 认证 + 完整性保护
=HTTPS
-
HTTP 协议与 TCP/IP 协议中间 加了一层HTTPS协议,HTTP -> TLS -> TCP -> IP
http存在问题
- 被窃听:使用了明文
- 遇伪装:不验证通信方身份
- 遭篡改:无法证明报文完整性
加密方式
- 对称密钥加密(共享密钥加密):加密解密用同一个密钥
- 密钥必须发给对方,过程中可能被窃听
- 非对称密钥加密(公开密钥加密):一把私有密钥(只有自己知道),一把公开密钥(任何人都可以知道)
- 发送方用公开密钥加密,接收方用私有密钥解密
- 处理速度较慢
HTTPS采用共享密钥加密
与公开密钥加密
混合加密机制,保证处理速度和安全性
- 交换密钥阶段: 使用公开密钥加密 可以简单理解为加密的是共享密钥的公钥(保证安全)
- 建立通信交换报文: 使用共享密钥加密 加密的是通信的报文(保证速度)
认证方式
- 数据传输被中间人窃听
- 就要加密传输,所以使用
对称加密
,加密解密相同密钥
- 就要加密传输,所以使用
- 数据无法被窃听了,但是双方为了约定对称加密算法和密钥,这些数据也要传输,
- 所以使用
非对称加密方式
加密对称加密的密钥
,公钥传给客户端用来加密,私钥服务端自己留着解密
- 所以使用
- 这样虽然中间人不能解密得到真正的数据,但是非对称加密的公钥可能会被篡改,
- 为了验证公钥的真实性,所以使用
第三方认证机构
, - 机构选择一种单向Hash算法(比如MD5)对公钥进行加密,生成摘要,单向Hash算法有一种特点就是单向不可逆的,这就防止了信息被篡改
- 机构会利用自己的私钥把摘要加密就生成了数字签名,数字签名和原有的公钥信息和在一起成为数字证书,把证书给客户端
- 客户端会拿事先放好的机构公钥对数字签名解密生成摘要,再利用hash算法对服务器传来公钥生成摘要进行比对认证,
- 为了验证公钥的真实性,所以使用
- 使用服务器的公开密钥加密报文发送
- 服务器用私有密钥解密报文
SSL和TLS
SSL
是一种安全传输协议,TLS
是SSL v3.0的升级版,目前所有的HTTPS都是用的是TLS
握手过程
借用【HTTP图解】中的过程(描述简化)
- 【Client Hello】客户端发送
支持的SSL的指定版本
、加密组件列表
(所使用的加密算法及密匙长度等)。 - 【Server Hello】服务器发送 支持的SSL版本以及加密组件
- 服务器发送 证书(数字签名+公钥)
- 【Server Hello Done】服务器发送【握手协商部分结束】
- 【Client Key Exchange】客户端回应,发送Pre-master secret(PMS)的随机密码串(已用公匙加密,认证过程也包含在内)
- 【Change Cipher Spec】客户端告诉服务端,我以后就用这个密钥(PMS)加密啦
- 【Finished】客户端发送报文,包含连接至今全部报文的整体校验值。这次握手协商是否能够成功,要以服务器是否能够正确解密该报文作为判定标准。
- 【Change Cipher Spec】服务器发送报文
- 【Finished】服务器发送报文
- 服务器和客户端的Finished报文交换完毕,SSL连建立完成,可以发送HTTP请求,受到SSL保护
- 应用层协议通信,即发送HTTP响应。
- 最后由客户端断开连接。断开连接时,发送close_notify报文。
六. 发起http请求(http1.1
, http2.0
)
- 构建请求行、请求头等信息, Cookie 等数据加到请求头中,向服务器发送请求信息
请求信息
- 请求行
- 请求方法
- 请求URI
- http协议版本
- 请求头
- 请求体
http1.1
-
复用链接
- 通过
Keep-Alive
持久化链接,只要浏览器或者服务器没有明确断开连接,那么该TCP连接会一直保持,所以处理完一个请求之后,可以用来处理下个http请求
- 通过
-
管线化
- 即可以同时
并行
发送多个请求, 不需要等待响应再发送下一个请求, - 但是服务器依然需要根据请求顺序来回复浏览器的请求,
- 并没有完美的解决
队头堵塞
(某个请求因为某些原因没有及时返回,那么就会阻塞后面的所有请求)问题
- 即可以同时
-
缓存处理
- 新增了缓存控制策略
Entity tag
,If-Unmodified-Since
,If-Match
,If-None-Match
(此处可看缓存
)
- 新增了缓存控制策略
-
带宽优化及网络连接的使用
- HTTP1.0中,存在浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,
- HTTP1.1则在请求头引入了
range头域
,它允许只请求资源的某个部分
,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
-
错误通知的管理
- 在HTTP1.1中新增了24个错误状态响应码,例如:409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除
-
Host头处理
- 在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此请求消息没有传递主机名
- 后来,一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。
- HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有报错400
SPDY
- 对HTTP1.优化
- HTTP -> SPDY -> SSL -> TCP -> ...
-
降低延迟,针对HTTP高延迟的问题,SPDY优雅的采取了多路复用(multiplexing)。多路复用通过多个请求stream共享一个tcp连接的方式,解决了HOL blocking的问题,降低了延迟同时提高了带宽的利用率。
-
请求优先级
- 可以给每个请求设置优先级,这样重要的请求就会优先得到响应。
- 比如浏览器加载首页,首页的html内容应该优先展示,之后才是各种静态资源文件,脚本文件等加载,这样可以保证用户能第一时间看到网页内容。
-
header压缩
SPDY
使用通用的DEFLATE
算法
-
HTTPS加密
- 提高传输数据的安全性
-
服务端推送
- 服务器可以对客户端的一个请求发送多个响应
http2.0
- 改善用户使用web速度体验,对HTTP1.优化 基于 SPDY 设计
-
二进制格式
- HTTP1.x 采用文本格式
- HTTP2.0 采用二进制格式,方便且健壮
-
多路复用
http2.0
虽然依然遵循请求-响应模式,但客户端发送多个请求和服务端给出多个响应的顺序不受限制,这样既避免了"队头堵塞",又能更快获取响应。- 例如:在复用同一个TCP连接时,服务器同时(或先后)收到了A、B两个请求,先回应A请求,但由于处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分
- 某个请求任务耗时严重,不会影响到其它连接的正常执行。
-
header压缩
- 专门为首部压缩而设计的
HPACK
算法
- 专门为首部压缩而设计的
-
服务端推送
- 基于SPDY
七. 服务器处理请求 (跨域
,XSS
,CSRF
)
- 发起请求非同源会报跨域问题,一般是
CORS
解决 - 中间可能会经过调度服务器
nginx
控制负载均衡 - 服务器接收到请求信息后,会根据请求信息生成响应数据
跨域
基本介绍
- 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源
跨域是由于浏览器的同源策略
同源策略 出于对浏览器安全考虑,防止收到XSS,CSRF攻击,协议+域名+端口有一个不同,则是非同源
同源限制策略
- cookie, localstroge, indexDB无法读取
- dom和js对象无法获取
- ajax请求不能发送
9种方法
- jsonp
- document.domain + iframe
- location.hash + iframe
- window.name + iframe
- postMessage
- CORS
- nginx代理
- nodejs中间件代理
- websocket
1.jsonp
- 定义:
jsonp
跨域数据交互协议
- 原理:
- Web页面上调用js文件时则不受跨域的影响,比如静态资源的加载(不仅如此,我们还发现凡是拥有src这个属性的标签都拥有跨域的能力,比如
<script>、<img>、<iframe>
- 应用:
-
为了远端js知道需要调用的函数,且可能会调用很多数据对象,对应不同的函数
-
js脚本就不能写死,我们动态生成js脚本,利用传参的不同,来区分调用
- 缺点:
- 只能实现get一种请求。
2.CORS
跨域资源共享(Cross-origin resource sharing)
对那些可能对服务器数据产生副作用的 HTTP 请求方法 浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request), 从而获知服务端是否允许该跨域请求 服务器确认允许之后,才发起实际的 HTTP 请求
不会触发CORS预检的请求称为简单请求
- 简单请求
-
GET,POST,HEAD
-
不得人为设置其他首部字段
-
content-type
仅限(text/plain
multipart/form-data
application/x-www-form-urlencoded
)
- 复杂请求(会触发预检请求)
普通跨域请求:服务端设置 Access-control-allow-origin
携带cookie跨域请求:
- 前端后端都需设置
- 前端:withcredentials
- 后端:Access-Control-Allow-Credentials: true
- 服务器不得设置 Access-Control-Allow-Origin 的值为*, 必须是某个具体的域名
3.nginx 代理跨域
- nginx 解决iconfont跨域
- 浏览器跨域访问js、css、img等常规静态资源被同源策略许可,
- 但iconfont字体文件(eot|otf|ttf|woff|svg)例外,
- 此时可在nginx的静态资源服务器中加入以下配置
location / {
add_header Access-Control-Allow-Origin *;
}
- nginx 反向代理接口跨域
原理:
同源策略是浏览器的安全策略,不是http协议的一部分,服务器端调用接口, 不会执行js脚本,不需要同源策略,也就不存在跨域问题
实现:
- 通过nginx配置代理服务器(与a同域名,不同端口)做跳板机
- 反向代理访问b接口,并修改cookie 中domain 信息,方便当前域cookie写入,实现跨域登录
nginx配置
server{
listen 81;
server_name www.a.com;
locaton / {
proxy_pass https://b.com
add_header Access-Control-Allow-Origin http://a.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
4.nodejs中间件代理跨域
原理:
- 大致与nginx相同,通过启动一个代理服务器,实现数据的转发
- 且可通过
cookieDomainRewrite
修改响应头中cookie 中域名,实现当前域cookie写入
5.webSocket协议跨域
websocket是H5新协议,实现浏览器与服务器全双工通信,同时允许跨域,
使用socket.io(websocket原生封装)
6.postMessage
postMessage
是h5 XHR的API,是跨域操作的window属性之一
解决
- 页面与新开窗口的数据传递
- 多窗口之间的消息传递
- 页面与iframe消息传递
- 以上三种场景的跨域传递
应用 postMessage(data, origin)
data: 需要传递的数据 JSON.String() 部分浏览器只支持字符串
origin: 协议+ 域名 + 端口 可以 *
传递任意窗口
iframe.contentWindow.postMessage
7. document.domain + iframe
仅限子域不同跨域 两个页面强制设置 document.domain 为基础主域,就实现了同域
8.location.hash + iframe
a与b不同域,b是a的iframe ,a能传给b ,但b无法传a,
利用c,c与a是同域,且c是b的iframe,b 传给c, c再传给a
实现了a与b跨域双向通信,
原理: a与b跨域通信,通过c来实现,不同域之间利用iframe的location.hash传值, c与a同域,所以c可通过parent.parent访问a页面所有对象。
实现: a -> b -> c -> a
9.window.name + iframe
原理:window.name 属性 再不同页面,甚至不同域名,加载之后依然存在,并支持 较大存储(2MB)
a,b跨域, b是a的iframe, 实现:
- 第一次,加载跨域页,并留存数据于window.name
- 成功后,改变 改变b的location为a的同域,使iframe.onload再次执行,
- 第二次,读取同域的window.name
小结
- CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方法。
- JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据
- 不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制
- 日常工作中,用的比较多的跨域方案还是cors和nginx反向代理
XSS(跨站脚本攻击)
-
通过在 有安全漏洞的web网站注册用户的浏览器上 运行非法的HTML标签或Javascript进行的一种攻击
-
本质是:恶意代码未经过滤,浏览器无法分辨,导致恶意代码被执行
影响
- 骗取用户个人信息
- 帮助攻击者发送恶意请求
- 显示伪造的文章
分类
- 存储型 XSS
恶意代码存在
数据库
,由服务端
取出,插入在html
执行 - 反射型 XSS
恶意代码存在
URL
,由服务端
取出,插入在html
执行 - DOM型 XSS
恶意代码存在
数据库/客户端/URL
,前端JavaScript
取出, 插入在JavaScript
执行
DOM型 XSS属于前端安全漏洞,其他两种属于后端安全漏洞
预防
预防存储型和反射型 XSS 攻击
- 前端渲染,把代码和数据分隔
明确渲染内容的类别,帮助浏览器分辨是文本(.innerText
),属性(.setAttribute
)或是样式(.style
),用对应的方法去渲染
适用于内部管理项目
- 转义 HTML
对 HTML 做充分转义
适用于要求性能,SEO的对外项目
预防 dom型 XSS 攻击
-
小心使用
.innerHtml
,.outerHtml
,document.write()
, 不要把不可信数据插入html 尽量使用.textCotent
,.setAttribute()
-
如果用vue/react框架,小心使用
v-html
/dangerouslySetInnerHTML
, 也可减少XSS隐患 -
以下都能把字符串作为代码运行,小心使用:
- dom内联事件监听器
location
,onclick
,onload
,onmouseover
等 <a>
标签的属性- javascript的
eval()
,setTimeout
,setInterval()
等
- dom内联事件监听器
其他 预防 XSS 攻击
-
Content Security Policy (CSP)内容安全策略
- 禁止加载外域代码,防止复杂的攻击逻辑
- 禁止外域提交,被攻击数据不会泄漏到外域
- 禁止内联脚本执行,
- 禁止未授权脚本执行
-
内容长度控制,防止复杂攻击
-
验证码,防止脚本提交
-
HTTP-only Cookie, 禁止js读取cookie
小结
- XSS预防 主要靠转义 且要保证
- 正确性 要防止多余和错误的转义,避免正常的用户输入出现乱码
- 完整性 需要在全部需要转义的位置,对数据进行对应的转义
- 针对性 针对不同的上下文调用不同的转义规则
CSRF(跨站请求伪造)
介绍
诱导受害者进入第三方网站,向被攻击网站发起请求,利用受害者在被攻击网站获取的注册凭证(cookie),绕过后台验证,以此冒充用户对被攻击网站执行某项操作。
本质:冒充用户,发起请求,执行恶意操作(凭证也不能证明请求是用户主动想要发起的)
特点
- 攻击一般发起在第三方网站,被攻击网站无法防范
- 攻击利用登录凭证,冒充用户,而不是直接盗取数据
- 攻击者并不能获取凭证,只是使用
- 攻击利用,URL,href,Form,等难以追踪
分类
- GET类型
访问第三方网站,恶意请求放在静态资源上,页面加载是就会自动发送http请求 例如
<img href="http...">
- POST类型
通常使用的是一个自动提交的表单,模拟用户完成POST操作,例如
<form action="http..."></form>
- 链接类型
诱导用户主动点击链接触发,例如
<a href="http...">
预防
针对CSRF特点
- 通常发生在第三方
- 攻击者不能获取到cookie 只是使用
作出专门预防
- 阻止不明外域访问
- 同源检测
- Samesite Cookie
- 提交时要求附加本域才可获取的信息
- token(CSRF攻击时不会自动带上token)
- 双cookie验证
同源检测
-
判断请求是否来自外域,利用请求的header属性
origin
和referer
-
缺点:
- 攻击者可以隐藏Referer
referrerpolicy="no-referrer
- IE低版本浏览器有问题
- HTTPS跳转HTTP Referer丢失
- flash跳转其他网站,Referer杂乱
- 攻击者可以隐藏Referer
-
阻止外域请求时,需要过滤掉正常的外战请求(例如百度搜索,友好链接)
Token
用户登录时,服务端返回 token,之后的请求都携带token,用以辨别正常请求和攻击请求
缺点:
- CSRF攻击时只是不会
自动
带上token, 如果存在XSS漏洞,攻击者还是可以写脚本(XSS)获取token,进而进行CSRF攻击 - 在会话中存储CSRF Token比较麻烦,而且不能在通用的拦截上统一处理所有的接口
双重Cookie
与token原理大致相同,
用户访问网站时,服务端向请求域注入一个cookie,随机字符,之后的请求都携带cookie,用以辨别正常请求和攻击请求
优点:
- 无需session存储,可统一拦截检验
缺点:
- 如果存在XSS漏洞,还是可获取cookie, 进而进行CSRF攻击
- 子域名无法隔离
- 前后端是不同子域,无法共享cookie,网站无法获取后端注入的cookie
- 如果cookie 注入在主域,那么其他子域也可修改此cookie
无法大规模应用
samesite cookie 属性
根源解决问题,改进http协议,set-cookie响应头添加samesite属性
-
Samesite=Strict 严格模式:非本域请求不会携带此cookie
-
Samesite=Lax 宽松模式:第三方请求,且同时是GET请求,可以携带
缺点:
- 设置为Strict时,子域名或者是新标签重新打开刚登陆的网站,cookie不会携带,体验差
- 不支持子域,每个子域名都需要用户重新登录一次
不成熟,有待发展
小结
- 目前业界针对CSRF的防御,一致的做法是使用Token
- 我们也可以通过
安全测试
,行为监控
,上传文件&内容校验
,提升网站安全性
八. 浏览器响应请求(响应信息
, 状态码
)
- 收到服务器返回的响应数据,
网络进程
开始解析响应头
- 如果发现返回的状态码是 301 或者 302,重定向到其他 URL,就重新发起
HTTP
请求
响应信息
- 响应行
- http协议版本
- 状态码
- 响应头
- 响应体
状态码
-
2XX 成功
- 200 OK,表示从客户端发来的请求在服务器端被正确处理
- 201 Created 请求已经被实现,而且有一个新的资源已经依据请求的需要而建立
- 202 Accepted 请求已接受,但是还没执行,不保证完成请求
- 204 No content,表示请求成功,但响应报文不含实体的主体部分
- 206 Partial Content,进行范围请求
-
3XX 重定向
- 301 moved permanently,永久性重定向,表示资源已被分配了新的 URL
- 302 found,临时性重定向,表示资源临时被分配了新的 URL
- 303 see other,表示资源存在着另一个 URL,应使用 GET 方法丁香获取资源
- 304 not modified,表示服务器允许访问资源,但因发生请求未满足条件的情况
- 307 temporary redirect,临时重定向,和302含义相同
-
4XX 客户端错误
- 400 bad request,请求报文存在语法错误
- 401 unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息
- 403 forbidden,表示对请求资源的访问被服务器拒绝
- 404 not found,表示在服务器上没有找到请求的资源
- 408 Request timeout, 客户端请求超时
- 409 Confict, 请求的资源可能引起冲突
-
5XX 服务器错误
- 500 internal sever error,服务器端执行时发生错误
- 501 Not Implemented 请求超出服务器能力范围,例如服务器不支持当前请求所需要的某个功能,或者请求是服务器不支持的某个方法
- 503 service unavailable,超负载或停机维护,无法处理请求
- 505 http version not supported 服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本
九. 断开TCP连接(四次挥手
)
- 数据传输全都完毕后,断开
TCP
链接
四次挥手
通信的双方都可释放连接,双方都处于 establised 状态,假如是客户端先发起关闭请求
- 第一次挥手:客户端发送一个
FIN
报文,指定序列号 此时客户端处于FIN_WAIT1
状态。 - 第二次握手:服务端收到后,会发送
ACK
报文,且把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于CLOSE_WAIT
状态。 - 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给
FIN
报文,且指定一个序列号。此时服务端处于LAST_ACK
的状态。 - 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值,此时客户端处于
TIME_WAIT
状态。等待计时器设置的两倍的最长报文段(MSL)时间
,以确保服务端收到自己的 ACK 报文之后才会进入CLOSED
状态 - 服务端收到 ACK 报文之后,就处于关闭连接了,处于
CLOSED
状态
'Client' 'Server'
【1】FIN seq=u
`FIN_WAIT1` --------------------->
【2】ACK,ack=u+1,seq=v
`FIN_WAIT2` <--------------------- `CLOSE_WAIT`
【3】FIN,ACK,seq=w,ack=u+1
`TIME_WAIT` <--------------------- `LAST_ACK`
【4】ACK ack=w+1, seq=u+1
`2MSL close` ---------------------> `close`
注意
-
第二次挥手完成后,TCP连接处于半关闭状态(客户端不再向服务器发送数据),服务器若发送数据,客户端仍然接受。
-
为什么连接的时候是三次握手,关闭的时候却是四次握手
- 当
Server
端收到FIN
报文时,可能数据还没有传输完成,而不会立即关闭链接, - 所以先回复一个
ACK
,传输完成在发起FIN
,故需要四步握手
- 当
-
等待2MSL (最长报文寿命)
- 为了保证客户端发送的最后一个ACK报文段能够到达服务器
- 防止“已失效的连接请求报文段”,出现在本连接中。
要确保服务器收到了
ACK
报文,没有收到的话,会重新发FIN
,客户端再次收到 FIN 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。
十. 渲染准备
- 浏览器为页面分配一个
渲染进程
浏览器进程
将网络进程
接收到的 HTML 数据提交给渲染进程
- 接下来进入渲染阶段
多页面公用一个渲染进程 ?
- 如果从一个页面打开了另一个新页面,而新页面和当前页面属于同一站点的话,那么新页面会复用父页面的渲染进程
十一. 构建dom树
- 将
HTML
转换为浏览器能够理解的结构DOM树
十二. 样式解析计算(css阻塞问题
,结构化
,属性标准化
,元素样式具体化(继承,层叠,特指)
))
- 同样的,把 CSS 转换为浏览器能够理解的结构
具体解析
- 把 CSS 转换为浏览器能够理解的结构
- 转换样式表中的属性值,使其标准化
- 计算出 DOM 树中每个节点的具体样式
-
继承
,层叠
,特指
,优先级由低到高 -
继承,某些父节点样式是可以继承给子节点的,例如,文本属性,
color
,font-size
例如字体颜色 -
层叠,定义了如何合并来自多个源的属性值的算法,多个来源,优先级由低到高
- 浏览器默认样式表
- 用户样式表(用户强制放大)
- 链接样式表
- 嵌入样式
- 行内样式
-
特指, 选择器优先级,由高到低,I-C-E, ID,class,element
-
css 阻塞问题
- css加载不会阻塞DOM树解析,但会阻塞布局树的构建
- css下载是异步的,但构建布局树是需要dom和css同时存在的
十三. 创建布局树(创建可见元素的布局树
,计算节点位置
,优化(重绘,重排)
)
- 创建布局树, 计算出
DOM
树中可见元素的几何位置
重绘和重排
- js动态修改了
DOM
或CSS
,就会导致重绘和重排 - 重绘:属性或样式发生改变,不影响布局,绘制及之后的阶段都会重新进行
- 重排:布局或者几何属性需要改变, 布局以及之后的阶段都会重新进行
优化 减少重排和重绘
-
浏览器:
-
浏览器大多通过队列机制更新布局,浏览器把修改操作放在队列中,一定时间后执行后才会清空队列,
-
但如果获取布局信息,避免队列中有影响这些属性或方法返回值的操作,浏览器会直接强制清空队列,触发重排与重绘保证正确的返回值
-
即避免使用获取布局信息的方法或属性,他们会强制渲染刷新队列
-
-
CSS
- transform 代替 top, 两者本无太大区别,但前者可以开启
GPU加速
- 动画效果应用绝对或固定定位,控制动画速度可使用
requestAnimationFrame
- CSS3
GPU加速
- transform 代替 top, 两者本无太大区别,但前者可以开启
requestAnimationFrame requestAnimationFrame采用系统时间间隔, 保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销; 也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果
- JS :
- 避免操作样式和dom
- 避免读取重排&重绘属性
- 动画绝对定位
十四. 分层
- 分层,生成图层树
分层
- 页面会有一些复杂效果,为了避免重绘和重排,提高渲染效率,会生成单独专用的图层
- 正常节点都在同一个默认图层里
生成单独图层的条件
-
拥有具有3D变换的CSS属性
-
视频解码的
<video>
节点 -
<canvas>
节点 -
CSS3动画
-
CSS加速属性的元素(will-change)
注意:创建一个单独图层的时,占用的内存也会大大增加
十五. 绘制
- 为图层树中每个图层生成绘制列表,交给合成线程
十六. 分块,光栅化
- 合成线程会将较大、较长的
图层
(当前屏幕无法完全显示)划分为图块
, 栅格化
将图块变成位图,栅格化过程
会使用GPU
来加速生成
分块原因
- 有的图层可以很大,但是通过视口,用户只能看到页面的一部分,
- 如果绘制出所有图层内容的话,开销很大,且无必要
位图
- 电脑绘制一张图片,首先是把这个图片表示为一种计算机能理解的数据结构, 即二维数组
- 数组的每个元素记录这个图片中的每一个像素的具体颜色
- 绘制的过程也就是往数组中具体的下标里填写像素
十七. 显示
- 合成线程发送绘制图块命令,给浏览器进程
- 浏览器生成页面,显示到显示器上
总结
总的来说两个部分:请求资源
和页面渲染
请求资源
- 用户输入
URL
, 浏览器解析判定为URL
,根据规则进行补全 - 查找本地有缓存且未过期,则使用本地,否则请求资源
- 进行
DNS
解析 反查域名,还会有DNS
缓存 - 如果是
HTTPS
请求还要先建立TLS
链接 - 建立
TCP
链接, 三次握手 - 发起
HTTP
请求 - 服务器处理,浏览器响应
- 数据请求完成,断开TCP链接,四次分手
页面渲染
- 解析HTML, 构建
DOM
树 - 解析CSS,构建
styleSheets
- 创建布局树,计算元素的位置
- 对布局分层,生成图层树
- 生成绘制列表给合成线程
- 合成线程把图层分成图块,并将图块转成位图
- 浏览器进程绘制显示