前端浏览器安全汇总

205 阅读20分钟

思维导图 浏览器原理.png

一、浏览器安全

1.什么是xss攻击?

(1)概念

xss攻击指的是跨站脚本攻击,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如cookie等。

xss的本质是因为网站没有对恶意代码进行过滤,与正常代码混合在一起了,浏览器无法分辨哪些脚本是可信的,从而导致恶意代码运行。

攻击方式:

  • 获取页面的数据,如Dom、cookie、localstorage
  • Dos攻击,发送合理请求,占用服务器资源,使用户无法访问服务器
  • 破坏页面结构
  • 流量劫持(将链接指向某网站)

(2)攻击类型

xss可分为存储型、反射型、DOM型:

  • 存储型指的是恶意脚本回存储在目标服务器上,当浏览器请求数据时,脚本从服务器传回并执行
  • 反射型指的是攻击者诱导用户访问一个带有恶意代码的url后,服务器端接受数据后处理,然后把带有恶意代码的数据发送给浏览器端,浏览器解析这段带有xss代码的数据后当做脚本执行,最终完成xss攻击
  • DOM型指的是通过修改页面DOM节点形成xss

2.如何防范xss攻击

  • 可以从浏览器的执行来进行预防,一种是使用纯前端的方式,不用服务器端拼接后返回(不使用服务端渲染)。另一种是对需要插入到 HTML 中的代码做好充分的转义。
  • 使用csp,指的是内容安全策略,本质是建立白名单,告诉浏览器哪些外部资源可加载和执行,从而防止恶意代码注入攻击
  • 对一些敏感信息进行保护,如cookie使用http-only,使脚本无法获取,也可以使用验证码,避免脚本伪装成用户的操作。

3.什么是csrf攻击?

(1)概念

csrf攻击指的是跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求,如果用户在被攻击网站中保存了登录状态,那么攻击者可以利用这个状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。

csrf本质是利用cookie会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。

攻击类型:

  • GET类型的csrf攻击:如在网站中的img标签里构建一个请求,当用户打开这个网站就会自动发起提交
  • POST类型的csrf攻击:如构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
  • 链接类型csrf攻击:比如在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击

4.如何防御csrf攻击

  • 进行同源检测
  • 使用 CSRF Token 进行验证
  • 在设置 cookie 属性的时候设置 Samesite ,限制 cookie 不能作为被第三方使用

5.进程和线程

  • 进程描述了cpu在运行指令及其加载和保存上下文需要的时间,放在应用上代表了一个程序
  • 线程是进程中最小单位,描述了执行一段指令需要的时间

进程是资源分配的最小单位,线程是cpu调度的最小单位

进程和线程间特点:

(1)进程中任意线程出错,都会导致整个进程崩溃 (2)线程之间共享进程中的数据 (3)但一个进程关闭后,操作系统会回收进程占用的资源 (4)进程间的内容相互隔离

Chrome浏览器的架构图

浏览器进程.awebp

从图中可以看出,最新的 Chrome 浏览器包括:

  • 一个浏览器主进程
  • 1个gpu进程
  • 1个网络进程
  • 多个渲染进程
  • 多个插件进程

这些进程功能:

  • 浏览器进程:主要负责界面显示、用户交互,子进程管理,同时提供存储功能
  • 渲染进程:核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,
  • GPU进程:UI 界面都选择采用 GPU 来绘制
  • 网络进程:主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
  • 插件进程:主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。

6.进程和线程区别

  • 进程可以看做独立应用,线程不能
  • 资源:进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位);线程是cpu调度的最小单位
  • 通信方面:线程间可以通过直接共享同一进程中的资源,而进程通信需要借助 进程间通信。
  • 调度:进程切换比线程切换的开销要大。线程是CPU调度的基本单位,线程的切换不会引起进程切换,但某个进程中的线程切换到另一个进程中的线程时,会引起进程切换。
  • 开销:由于创建或撤销进程时,系统都要为之分配或回收资源,如内存、I/O 等,其开销远大于创建或撤销线程时的开销。同理,在进行进程切换时,涉及当前执行进程 CPU 环境还有各种各样状态的保存及新调度进程状态的设置,而线程切换时只需保存和设置少量寄存器内容,开销较小。

7.浏览器渲染进程的线程有哪些

浏览器的渲染进程的线程总共有五种:

浏览器渲染线程.awebp (1)GUI渲染线程 负责渲染浏览器页面,解析HTML、CSS,构建DOM树、构建CSSOM树、构建渲染树和绘制页面;当界面需要重绘或由于某种操作引发回流时,该线程就会执行。

(2)JS引擎线程 JS引擎线程也称为JS内核,负责处理Javascript脚本程序,解析Javascript脚本,运行代码

(3)时间触发线程属于浏览器而不是JS引擎,用来控制事件循环;当JS引擎执行代码块如setTimeOut时(也可是来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件触发线程中;当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理;

(4)定时器触发进程即setInterval与setTimeout所在线程;浏览器定时计数器并不是由JS引擎计数的,因为JS引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确性;因此使用单独线程来计时并触发定时器,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行,所以定时器中的任务在设定的时间点不一定能够准时执行,定时器只是在指定时间点将任务添加到事件队列中;

(5)异步http请求线程

  • XMLHttpRequest连接后通过浏览器新开一个线程请求;
  • 检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中,等待JS引擎空闲后执行

8.如何实现多个浏览器标签页之间通信

    • 使用 websocket 协议,因为 websocket 协议可以实现服务器推送,所以服务器就可以用来当做这个中介者。标签页通过向服务器发送数据,然后由服务器向其他标签页推送转发。
    • 使用 localStorage 的方式,我们可以在一个标签页对 localStorage 的变化事件进行监听,然后当另一个标签页修改数据的时候,我们就可以通过这个监听事件来获取到数据。这个时候 localStorage 对象就是充当的中介者的角色。
  • 使用 postMessage 方法,如果我们能够获得对应标签页的引用,就可以使用postMessage 方法,进行通信。

三、浏览器缓存

1.对浏览器缓存机制理解

浏览器缓存的全过程:

  • 浏览器第一次加载资源,服务器返回200,浏览器从服务器下载资源文件,并缓存资源文件与response header,以供下次加载时候使用
  • 下一次加载资源时,如果强制缓存优先级较高,显比较当前时间和上一次请求返回200时的时间差,如果没有超过cache-control设置的max-age,则没有过期,命中强缓存,直接从本地读取资源。如果浏览器不支持HTTP1.1,则使用 expires 头判断是否过期
  • 如果资源已过期,则表明强制缓存没有被命中,则开始协商缓存,向服务器发送带if-none-match和if-modified-since的请求
  • 服务器收到请求后,优先根据Etag的值判断被请求的文件有没有修改,Etag值一致就没有修改,命中协商缓存,返回304;如果不一致有改动,直接返回新的文件资源带上新的Etag值返回200
  • 如果服务器收到请求没有Etag值,则判断if-modified-since和被请求的文件的最后修改时间对比,一致则命中协商缓存,返回304,不一致则返回新的last-modified和文件并返回200

浏览器缓存加载图.awebp

2.协商缓存和强缓存的区别

(1)相同点

  • 缓存命中时都会直接使用本地的缓存副本
  • 缓存不命中时,都会向服务器发送请求

(2)不同点

强缓存如果浏览器根据请求信息判断,如果命中强缓存,则不会发送请求到服务器,直接使用本地资源

(3)总结

强缓存策略和协商缓存策略在缓存命中时都会直接使用本地的缓存副本,区别只在于协商缓存会向服务器发送一次请求。它们缓存不命中时,都会向服务器发送请求来获取资源。在实际的缓存机制中,强缓存策略和协商缓存策略是一起合作使用的。浏览器首先会根据请求的信息判断,强缓存是否命中,如果命中则直接使用资源。如果不命中则根据头信息向服务器发起请求,使用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器直接使用本地资源的副本,如果协商缓存不命中,则浏览器返回最新的资源给浏览器。

四、浏览器渲染原理

1.浏览器的渲染过程

  • 首先解析收到的文档,根据文档定义构建一颗Dom树,DOM树是由DOM元素和属性节点组成的
  • 然后对css解析,生成CSSDOM规则树
  • 根据DOM树和CSSDOM规则树构建渲染树,渲染树的节点称为渲染对象
  • 当渲染对象被创建并添加到渲染树中,它们并没有大小和位置,所以浏览器生成渲染树后,会根据渲染树来进行布局(也叫回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
  • 布局阶段结束后是绘制阶段,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。

大致过程如图所示:

浏览器渲染原理图片.awebp

注意:  这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html 都解析完成之后再去构建和布局 render 树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。

2.浏览器渲染优化

(1)JavaScript既会阻塞HTML的解析,也会阻塞CSS的解析。因此我们可以对JavaScript的加载方式进行改变,来进行优化,尽量将JavaScript文件放在body的最后 (2)<script>标签的引入资源方式有三种,有一种就是我们常用的直接引入,还有两种就是使用 async 属性和 defer 属性来异步引入,两者都是去异步加载外部的JS文件,不会阻塞DOM的解析(尽量使用异步加载)。三者的区别如下:

  • script立即停止页面渲染去加载资源文件,当资源文件加载完毕立即执行js代码,js执行完毕后继续渲染页面
  • async 时在下载完成后,立即异步加载,加载后立即执行,多个带async属性的标签,不能保证加载顺序
  • defer 时在下载完成后,立即异步加载,加载后,如果dom树还没构建好,则先等dom树解析好在执行,如果dom树已经准备好则立即执行,多个defer属性标签,按照顺序执行

(3)减少回流和重绘

  • 操作DOM时,尽量在低层级的DOM节点进行操作
  • 不要频繁操作元素的样式
  • 使用absolute或者fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素
  • 避免频繁操作dom,可以创建一个文档碎片documentFragment,在它上面应用所有dom操作,最后把它添加到文档中
  • 开启css3的硬件加速(只会重绘,不会回流)

五、浏览器本地存储

1.浏览器本地存储方式及使用场景

(1)cookie

特性:

  • Cookie的大小只有4kb,它是一种纯文本文件,每次发起HTTP请求都会携带Cookie。
  • Cookie是无法跨域名的,也就是说a域名和b域名下的cookie是无法共享的,这也是由Cookie的隐私安全性决定的,这样就能够阻止非法获取其他网站的Cookie

如果需要域名之间跨域共享Cookie,使用Nginx反向代理

Cookie的使用场景: 最常见的使用场景就是Cookie和session结合使用,我们将sessionId存储到Cookie中,每次发请求都会携带这个sessionId,这样服务端就知道是谁发起的请求,从而响应相应的信息。

(2)localstorage

特征:

  • LocalStorage的大小一般为5MB,可以储存更多的信息
  • LocalStorage是持久储存,并不会随着页面的关闭而消失,除非主动清理,不然会永久存在
  • 仅储存在本地,不像Cookie那样每次HTTP请求都会被携带

LocalStorage的使用场景:

  • 有些网站有换肤的功能,这时候就可以将换肤的信息存储在本地的LocalStorage中,当需要换肤的时候,直接操作LocalStorage即可
  • 在网站中的用户浏览信息也会存储在LocalStorage中,还有网站的一些不常变动的个人信息等也可以存储在本地的LocalStorage中

(3)sessionstorage

SessionStorage和LocalStorage都是在HTML5才提出来的存储方案,SessionStorage 主要用于临时保存同一窗口(或标签页)的数据,刷新页面时不会删除,关闭窗口或标签页之后将会删除这些数据。 SessionStorage与LocalStorage对比:

  • SessionStorage和LocalStorage都在本地进行数据存储
  • SessionStorage也有同源策略的限制,但是SessionStorage有一条更加严格的限制,SessionStorage只有在同一浏览器的同一窗口下才能够共享
  • sessionstorage关闭窗口或标签页之后将会删除这些数据。

2.Cookie有哪些字段,作用分别是什么

  • Name:cookie的名称
  • Value:cookie的值,对于认证cookie,value值包括web服务器所提供的访问令牌;
  • Size: cookie的大小
  • Path:可以访问此cookie的页面路径。 比如domain是abc.com,path是/test,那么只有/test路径下的页面可以读取此cookie。
  • Domain:可以访问该cookie的域名,Cookie 机制并未遵循严格的同源策略,允许一个子域可以设置或获取其父域的 Cookie。当需要实现单点登录方案时,Cookie 的上述特性非常有用,然而也增加了 Cookie受攻击的危险,比如攻击者可以借此发动会话定置攻击。因而,浏览器禁止在 Domain 属性中设置.org、.com 等通用顶级域名、以及在国家及地区顶级域下注册的二级域名,以减小攻击发生的范围。
  • HTTP: 该字段包含HTTPOnly 属性 ,该属性用来设置cookie能否通过脚本来访问,默认为空,即可以通过脚本访问。在客户端是不能通过js代码去设置一个httpOnly类型的cookie的,这种类型的cookie只能通过服务端来设置。该属性用于防止客户端脚本通过document.cookie属性访问Cookie,有助于保护Cookie不被跨站脚本攻击窃取或篡改。但是,HTTPOnly的应用仍存在局限性,一些浏览器可以阻止客户端脚本对Cookie的读操作,但允许写操作;此外大多数浏览器仍允许通过XMLHTTP对象读取HTTP响应中的Set-Cookie头。
  • Expires/Max-size : 此cookie的超时时间。若设置其值为一个时间,那么当到达此时间后,此cookie失效。不设置的话默认值是Session,意思是cookie会和session一起失效。当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此cookie失效。

六、浏览器同源策略

1.什么是同源策略

跨域问题其实就是浏览器的同源策略造成的。

同源策略限制了从同一个源加载的文档或脚本如何与另一个源的资源进行交互。
这是浏览器的一个用于隔离潜在恶意文件的重要的安全机制。
同源指的是:**协议****端口号****域名**必须一致。

同源策略:protocol(协议)、domain(域名)、port(端口)三者必须一致。

同源政策主要限制了三个方面:

  • 当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。
  • 当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。
  • 当前域下 ajax 无法发送跨域请求。

2. 如何解决跨越问题

(1)CORS

(2)postMessage 跨域

(3)nginx代理跨域

(4)WebSocket协议跨域

七、浏览器事件机制

1.事件是什么?

事件是用户操作网页时发生的交互动作,比如 click/move, 事件除了用户触发的动作外,还可以是文档加载,窗口滚动和大小调整。事件被封装成一个 event 对象,包含了该事件发生时的所有相关信息( event 的属性)以及可以对事件进行的操作( event 的方法)。

2.如何阻止事件冒泡

  • 普通浏览器使用:event.stopPropagation()
  • IE浏览器使用:event.cancelBubble = true;

3.对事件委托的理解

(1)事件委托的概念

事件委托本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件委托(事件代理)。

(2)事件委托的特点

  • 减少内存消耗 如果有一个列表,列表之中有大量的列表项,需要在点击列表项的时候响应一个事件:
<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  ......
  <li>item n</li>
</ul>

如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能。因此,比较好的方法就是把这个点击事件绑定到他的父层,也就是 ul 上,然后在执行事件时再去匹配判断目标元素,所以事件委托可以减少大量的内存消耗,节约效率。

4.同步和异步的区别

  • 同步指的是当一个进程在执行某个请求时,如果这个请求需要等待一段时间才能返回,那么这个进程会一直等待下去,直到消息返回为止再继续向下执行。
  • 异步指的是当一个进程在执行某个请求时,如果这个请求需要等待一段时间才能返回,这个时候进程会继续往下执行,不会阻塞等待消息的返回,当消息返回时系统再通知进程进行处理

5.对事件循环的理解

因为 js 是单线程运行的,在代码执行时,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。在执行同步代码时,如果遇到异步事件,js 引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当异步事件执行完毕后,再将异步事件对应的回调加入到一个任务队列中等待执行。任务队列可以分为宏任务队列和微任务队列,当当前执行栈中的事件执行完毕后,js 引擎首先会判断微任务队列中是否有任务可以执行,如果有就将微任务队首的事件压入栈中执行。当微任务队列中的任务都执行完成后再去执行宏任务队列中的任务。

Event Loop 执行顺序如下所示:

  • 首先执行同步代码,这属于宏任务
  • 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
  • 执行所有微任务
  • 当执行完所有微任务后,如有必要会渲染页面
  • 然后开始下一轮 Event Loop,执行宏任务中的异步代码

6.宏任务和微任务分别有哪些

  • 微任务包括: promise 的回调、node 中的 process.nextTick
  • 宏任务包括: script 脚本的执行、setTimeout ,setInterval ,setImmediate 一类的定时事件,还有如 I/O 操作、UI 渲染等。