关于浏览器的那些事~

371 阅读8分钟

1.浏览器缓存

强缓存

第一次请求后,服务器会把过期时间通过响应头中的ExpiresCache-Control(优先考虑)字段告诉浏览器,通过这两个字段,浏览器在后面的请求发送前,会去检查这两个字段,判断该资源是否过期,如果没有过期则直接使用,否则发送请求。

(1)Expires

Expires即过期时间,存在于服务器返回的响应头中,告诉浏览器在这个日期之前可以直接使用该资源。

//表示资源在2019年11月22日8点41分过期,过期了就得向服务器发请求
Expires: Wed, 22 Nov 2019 08:41:00 GMT

缺点:服务器的时间可能和浏览器的时间不一致,所以这个过期时间可能是不准确的,因此这种方式在HTTP1.1版本中被抛弃了。

(2)Cache-Control

基于Expires的缺点,HTTP1.1版本提出了Cache-ControlCache-Control通过里面的max-age字段来控制缓存的过期时间。

Cache-Control常见属性

  • max-age:缓存过期时长,单位为秒
  • max-stale:(保鲜时长,个人比喻说法)假设其值为100,则表示:缓存过期后的100秒内,依然可以拿来使用
  • max-fresh:与上一字段相反,缓存到期时间还剩100秒就不能使用了,不新鲜了。
  • no-cache:不能使用强缓存,直接进入协商缓存
  • no-store:不使用缓存
  • public:可以被任何终端缓存,包括代理服务器,CDN
  • private:只能被浏览器终端缓存
  • s-maxage:优先级高于max-age,适用于共享缓存(如CDN)
协商缓存

当强缓存失效后,浏览器发送请求,并在请求头中携带相应的缓存tag ,这样的tag分为两种:If-Modified-SinceIf-None-Match,服务器接受后,根据其具体数值来决定是否使用缓存

(1)If-Modified-Since

第一次请求资源时,服务器会在响应头中返回Last-Modified字段,浏览器接收后,在以后的请求中,会在请求头中添加If-Modified-Since字段,服务器接收后,会通过对比自身的Last-Modified,来决定是否使用缓存

如果是一样的,就说明文件没有被更新过,则返回状态码304和空响应体

如果不一样,说明资源更新了,则返回对应资源,并在响应头中携带新的Last-Modified字段

(2)If-None-Match

同理,第一次请求后,服务器会在响应头中返回Etag字段,该字段是当前资源文件的唯一标识符,只要文件发生变化,都会重新生成。

浏览器再次请求时,在请求头中携带If-None-Match字段,服务器通过对比该字段与自身的Etag字段,来判断是否使用缓存。

2.缓存位置

前面我们提到,当强缓存命中或者服务器端返回304后我们直接从缓存中获取资源,那么这些资源在哪里呢?

浏览器的缓存位置一共有四种,按优先级从高到底排列分别是:

  • Service Worker
  • Memory Cache
  • Disk Cache
  • Push Cache

3.浏览器本地存储:Cookie,localStorage和sessionStorage

(1)Cookie

HTTP协议是一个无状态的协议,Cookie最开始被设计出来其实并不是来做本地存储的,而是为了弥补HTTP在状态管理上的不足。内部通过键值对来进行存储。

相关字段

  • Path:可以访问该Cookie的路径
  • httpOnly:表示禁止通过JS访问Cookie,减少XSS攻击
  • Secure:只能在https请求中才能携带
  • SameSite:规定浏览器不能在跨域请求中携带Cookie,减少CSRF攻击
  • Domain:域名,跨域或者Cookie的白名单
  • Expires/Max-size:过期时间

缺点

  • 容量缺陷:Cookie的体积上限只有4kb
  • 性能缺陷:Cookie紧跟域名,不管域名下面某一个地址需不需要这个Cookie,请求后悔携带上完整的Cookie,造成性能浪费
  • 安全缺陷:Cookie以纯文本的形式在浏览器和服务器中进行传输,很容易被非法用户截获
(2)localStorage

localStorage有一点跟Cookie一样,就是针对同一个域名,会存储相同的一段localStorage

特点

  • 容量:localStorage的容量上限为5M
  • 只存在客户端,默认不参与与服务端的通信
  • 存储方式:以键值对的方式存储,并且都只能是字符串形式,因此一些其它数据类型需要通过JSON.stringify进行转换,再通过setItem进行存储,以及通过getItem进行获取,removeItem进行删除操作
let obj = { name: 'chj', age: '18' };
//存储
localStorage.setItem('info', JSON.stringify(obj));

//获取
let me = JSON.parse(localStorage.getItem('info'));
//删除
localStorage.removeItem('info');
(3)sessionStorage

这个和上面的localStorage差不多,唯一的区别就是,sessionStorage的存储是会话级别的,也就是说,页面关闭后,sessionStorage就不存在了,下次打开里面不会存有上次保存的内容

4.从输入URL到页面呈现发生了什么--网络请求

(1)构建请求

浏览器进程接收到用户输入的URL请求,通过进程间通信(IPC)把URL发送给网络进程

(2)查找强缓存

网络进程接收到后,先检查强缓存,如果命中,直接使用缓存,否则进入下一步

(3)DNS解析

这里DNS的查找如下:操作系统首先会查找hosts文件中是否有记录,有的话将相应的IP直接返回,否则去本地dns解析器找,看有没有缓存,如果没有就去找计算机上配置的dns服务器,还没有的话,就去根DNS服务器(全球13台,固定IP地址)查找。

(4)建立TCP连接

接着,发送请求建立连接,同时Chrome有个机制,同一个域名下同时最多只能建立6个TCP连接,超过的需要进入排队等待状态。

TCP连接经历:

  1. 通过三次握手建立客户端与服务器端的连接
  2. 进行数据传输
  3. 通过四次挥手断开连接
(5)发送HTTP请求

TCP连接建立完毕后,客户端就可以和服务端开始通信,发送HTTP请求,HTTP请求携带:请求行,请求头和请求体。

(6)网络响应

HTTP请求到达服务器后,服务器进行处理,最后把数据传给浏览器,也就是网络响应,具体有三个部分:响应行,响应头和响应体

此时是否要断开连接,由请求头或者响应头的Connection字段来决定,如果其包含Connection: Keep-Alive,表示建立了持久连接,则不会马上断开。

(7)渲染

当浏览器接收到响应后,如果响应头中的Content-Typetext/html,那么接下来就要进入浏览器的解析和渲染工作了。

5.从输入URL到页面呈现发生了什么--解析渲染

当浏览器接收到响应后,如果响应头中的Content-Typetext/html,那么接下来就要进入浏览器的解析和渲染工作了。

  • 第一步是处理HTML标记并构造DOM树,解析器进行解析过程中,发现非阻塞资源,例如一张图片,浏览器会请求这些资源,并且继续解析;当遇到一个CSS文件时,解析也可以继续进行。但是,对于<script>标签(特别是没有async或着defer属性)会阻塞渲染并停止HTML的解析。
  • 第二步就是构造CSSOM树,在解析CSS文件时,并不会阻塞DOM树的构建,但是由于一些
  • 然后,将DOM树和CSSOM组合成一个Render树,计算样式树或渲染树从DOM树的根开始构建,遍历每个可见节点。
  • 接着,在渲染树上运行布局以计算每个节点的几何体,布局是确定呈现树中所有节点宽度、高度和位置,以及确定页面上每个对象的大小和位置的过程。
  • 最后,将每个节点绘制到屏幕上。

6.浏览器的回流和重绘

回流:

当我们对DOM的宽度、高度、位置等等几何尺寸进行修改时,浏览器需要重新计算元素的几何属性,此时其它元素的几何属性和位置也会因此受到影响,然后再将计算的结果绘制出来,这个过程叫做回流

重绘:

当我们对DOM的修改导致了样式的变化,但并没有影响几何属性(比如修改颜色)时,浏览器并不需要重新计算元素的几何属性,只需要为改变的元素绘制新的样式,这个过程叫做重绘。

由此我们可以看出,重绘不一定导致回流,但回流一定导致重绘

我们如何避免呢?

CSS

  • 避免使用CSS表达式(例如:calc())
  • 将动画效果应用到position属性为absolute或者fixed的元素上

JavaScript

  • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class,并一次性更改class属性
  • 避免频繁操作DOM,创建一个documentFragment,再它上面应用所有DOM操作,最后再把它添加到文档中。

7.浏览器跨域

九种跨域方式

其实,总结一下,常见的就三种:

  • jsonp:

    原理:利用<script>标签没有跨域限制的漏洞,网页可以得到从其它来源产生的JSON数据

    特点:兼容性好

    缺点:仅支持get方法,具有局限性,不安全,容易遭受XSS攻击

  • CORS:

    通过设置服务器端的Access-Control-Allow-Origin字段开启CORS,来进行跨域

  • nginx:

    原理:搭建一个中转服务器,由于跨域问题只是存在于浏览器,因此只需要通过nginx配置一个代理服务器(域名与浏览器相同,再反向代理访问服务器端)

8.浏览器安全

XSS攻击

XSS全称是Cross Site Scripting(跨站脚本),为了和CSS区分,于是命名为XSS。XSS攻击是指浏览器执行恶意脚本,从而拿到用户的信息并进行操作。

XSS攻击主要有三种方式:

存储型

就是将恶意脚本存储了,并且一般都是存储在了服务器端的数据库,然后在客户端执行这些脚本,从而达到攻击的目的。

常见的场景就是留言评论区提交一段脚本代码,如果前后端没有做转义工作,那么这些内容将存储到数据库,在页面渲染时会直接执行,这种攻击造成的影响是比较大的。

反射型

反射型XSS攻击指的是将恶意脚本作为网络请求的一部分

比如:

http://chj.com?q=<script>alert("哈哈哈")</script>

这样,在服务器端会拿到q参数,然后将内容返回给浏览器端,浏览器将这些内容作为HTML的一部分,发现是一个脚本,直接执行,就被攻击了。

不过,这并不会存储这些恶意脚本,相对来说影响较小。

文档型

文档型的XSS攻击并不会经过服务器,而是作为中间人的角色,在数据传输过程种劫持网络数据包,修改里面的HTML文件

预防措施

  • 对用户输入的内容进行转码或者过滤,让其不可执行
  • 利用Cookie的HttpOnly属性,防止窃取Cookie
CSRF攻击

CSRF(Cross-Site Request Forgery),跨站请求伪造,指的是黑客诱导用户点击链接,进入第三方网站,然后利用用户目前的登录状态发起跨站请求。

发起CSRF攻击有三个必要条件:

  1. 目标网站一定有CSRF漏洞
  2. 用户登录过目标网站,并且浏览器保存了登录状态
  3. 需要用户主动打开第三方站点

主要三种攻击类型:

  • 自动发GET类型:比如img标签,当用户打开这个网站会自动发起带Cookie 的资源请求

    <img src="恶意网址">
    
  • 自动发POST类型:比如一个隐藏的表单,在用户进入页面的时候会自动提交表单

    <form id="hack" action="恶意网址" methods="post">
     	...
     </form>
     <srcript>
     	document.getElementById('hack').submit();
     </script>
    
  • 诱导链接型:就是诱导用户主动点击链接

防范措施

  • 在Cookie种添加SameSite属性,其中有三个值:

    strict:严格模式,严禁第三方请求携带Cookie

    lax:这种模式比较宽松,只能在get方法提交表单或者a标签发送get请求的情况下才可以携带Cookie

    None:默认模式,请求会自动携带上Cookie

  • Token验证:

    当登录成功后,服务器除了返回Cookie,还会返回一个token,在后面的Cookie验证过程中,都需要携带这个token进行验证