likes
comments
collection
share

前端面试系列之浏览器篇

作者站长头像
站长
· 阅读数 25

之前整理了前端面试相关的知识,分享给大家!

注:很多内容来源于网络,侵权联系删除哈!

什么是 CSRF 攻击?

  • (1)概念

    • CSRF 攻击指的是跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求。如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。
    • CSRF 攻击的本质是利用 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。
  • (2)攻击类型

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

    • 进行同源检测,服务器根据 http 请求头中 origin 或者 referer 信息来判断请求是否为允许访问的站点,从而对请求进行过滤。当 origin 或者 referer 信息都不存在的时候,直接阻止请求。这种方式的缺点是有些情况下 referer 可以被伪造,同时还会把搜索引擎的链接也给屏蔽了。所以一般网站会允许搜索引擎的页面请求,但是相应的页面请求这种请求方式也可能被攻击者给利用。(Referer 字段会告诉服务器该网页是从哪个页面链接过来的)
    • 使用 CSRF Token 进行验证,服务器向用户返回一个随机数 Token ,当网站再次发起请求时,在请求参数中加入服务器端返回的 token ,然后服务器对这个 token 进行验证。这种方法解决了使用 cookie 单一验证方式时,可能会被冒用的问题,但是这种方法存在一个缺点就是,我们需要给网站中的所有请求都添加上这个 token,操作比较繁琐。还有一个问题是一般不会只有一台网站服务器,如果请求经过负载平衡转移到了其他的服务器,但是这个服务器的 session 中没有保留这个 token 的话,就没有办法验证了。这种情况可以通过改变 token 的构建方式来解决。
    • 对 Cookie 进行双重验证,服务器在用户访问网站页面时,向请求域名注入一个Cookie,内容为随机字符串,然后当用户再次向服务器发送请求的时候,从 cookie 中取出这个字符串,添加到 URL 参数中,然后服务器通过对 cookie 中的数据和参数中的数据进行比较,来进行验证。使用这种方式是利用了攻击者只能利用 cookie,但是不能访问获取 cookie 的特点。并且这种方法比 CSRF Token 的方法更加方便,并且不涉及到分布式访问的问题。这种方法的缺点是如果网站存在 XSS 漏洞的,那么这种方式会失效。同时这种方式不能做到子域名的隔离。
    • 在设置 cookie 属性的时候设置 Samesite ,限制 cookie 不能作为被第三方使用,从而可以避免被攻击者利用。Samesite 一共有两种模式,一种是严格模式,在严格模式下 cookie 在任何情况下都不可能作为第三方 Cookie 使用,在宽松模式下,cookie 可以被请求是 GET 请求,且会发生页面跳转的请求所使用。

XSS 攻击

  • 什么是 XSS 攻击?

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

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

    • 对一些敏感信息进行保护,比如 cookie 使用 http-only,使得脚本无法获取。也可以使用验证码,避免脚本伪装成用户执行一些操作。

    • 使用 CSP ,CSP 的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行,从而防止恶意代码的注入攻击。

      • CSP 指的是内容安全策略,它的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截由浏览器自己来实现。
      • 通常有两种方式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的方式
    • 转义特殊字符

进程与线程的概念

  • 从本质上说,进程和线程都是 CPU 工作时间片的一个描述:

    • 进程描述了 CPU 在运行指令及加载和保存上下文所需的时间,放在应用上来说就代表了一个程序。
    • 线程是进程中的更小单位,描述了执行一段指令所需的时间。
  • 进程是资源分配的最小单位,线程是CPU调度的最小单位。

  • 进程和线程之间的关系有以下四个特点:

  • (1)进程中的任意一线程执行出错,都会导致整个进程的崩溃。

  • (2)线程之间共享进程中的数据。

  • (3)当一个进程关闭之后,操作系统会回收进程所占用的内存, 当一个进程退出时,操作系统会回收该进程所申请的所有资源;即使其中任意线程因为操作不当导致内存泄漏,当进程退出时,这些内存也会被正确回收。

  • (4)进程之间的内容相互隔离。 进程隔离就是为了使操作系统中的进程互不干扰,每一个进程只能访问自己占有的数据,也就避免出现进程 A 写入数据到进程 B 的情况。正是因为进程之间的数据是严格隔离的,所以一个进程如果崩溃了,或者挂起了,是不会影响到其他进程的。如果进程之间需要进行数据的通信,这时候,就需要使用用于进程间通信的机制了。

对浏览器的缓存机制的理解?强缓存和协商缓存?静态文件添加版本号是为什么?

  • 浏览器第一次加载资源,服务器返回 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;
  • 很多网站的资源后面都加了版本号,这样做的目的是:每次升级了 JS 或 CSS 文件后,为了防止浏览器进行缓存,强制改变版本号,客户端浏览器就会重新下载新的 JS 或 CSS 文件 ,以保证用户能够及时获得网站的最新更新。

浏览器的渲染过程

  • 具体有这些过程:构建 DOM 树、CSSOM、布局阶段、分层、绘制、分块、光栅化和合成。渲染进程将 HTML 内容转换为浏览器可以理解的 DOM 树。

  • 渲染进程将 CSS 样式转化为 CSSOM,计算出DOM节点样式,这两个过程是并行的。

  • 创建布局树 layout tree,计算布局信息。

  • 对布局树进行分层,生成分层树 layer tree。

  • 每个图层生成绘制列表,并提交到合成线程。

    • 绘制列表是记录绘制指令的列表,比如每个元素的背景、边框都是一条单独的指令。
  • 合成线程将图层分成图块,并在光栅化线程池中将「图块转化为位图」(栅格化)。

    • 图块:把整个浏览器分成小块,方便浏览器先加载「可视区」。
    • 位图:也叫栅格图像,是由像素的单个点组成。这些点可以进行不同的排列和染色以构成图样。
    • 这个过程会使用 GPU 加速。
  • 合成线程发送绘制图块命令给浏览器进程。

  • 浏览器进程根据命令生成页面,并显示到显示器上。

  • 其他答案

    • HTML解析文件,生成DOM Tree,解析CSS文件生成CSSOM Tree
    • 将Dom Tree和CSSOM Tree结合,生成Render Tree(渲染树)
    • 根据Render Tree渲染绘制,将像素渲染到屏幕上。

css 加载会造成阻塞吗?

  • 在 Chrome 上,css 加载不会阻塞 HTML 解析,但会阻塞 DOM 渲染。

    • html 和 css 是同时解析的,所以不阻塞 HTML 解析。
    • html 生成 dom,css 生成 cssom,两者结合才能生成 render tree 渲染树,所以阻塞 DOM 渲染
  • css 加载会阻塞后面 js 的执行。 由于 JavaScript 是可操纵 DOM 和 css 的,如果在修改这些元素属性同时渲染界面,会造成冲突。

    • 为了防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系。所以 css 会阻塞后面 js 的执行。
  • 其他答案

    • DOM解析和CSS解析是两个并行的进程,所以这也解释了为什么CSS加载不会阻塞DOM的解析。
    • 然而,由于Render Tree是依赖于DOM Tree和CSSOM Tree的,所以他必须等待到CSSOM Tree构建完成,也就是CSS资源加载完成(或者CSS资源加载失败)后,才能开始渲染。因此,CSS加载是会阻塞Dom的渲染的。
    • 由于js可能会操作之前的Dom节点和css样式,因此浏览器会维持html中css和js的顺序。因此,样式表会在后面的js执行前先加载执行完毕。所以css会阻塞后面js的执行。

重排(回流)和重绘有什么区别,什么会引发重排,什么会引发重绘

  • 重排(回流):当渲染树中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。每个页面至少需要一次的回流,也就是页面第一次加载的时候。

  • 引发重排的原因

      1. 页面首次渲染
      1. 浏览器窗口大小发生改变
      1. 元素尺寸或位置发生改变
      1. 元素内容变化(文字数量或图片大小等)
      1. 元素字体大小变化
      1. 添加或者删除可见的 DOM 元素
      1. 激活 CSS 伪类(:hover)
      1. 设置 style 属性
      1. 查询某些属性或调用某些方法
  • 重绘:当页面元素样式的改变并不影响他在文档流中的位置时,这些属性只是影响元素的外观,风格,而不会影响布局的,浏览器会将新样式赋予给元素并重新绘制它,这个过程称之为重绘。

  • 原因:

      1. 字体颜色改变
      1. 边框样式改变
      1. 背景颜色、图片、定位、尺寸改变
      1. 外边框颜色、样式
      1. 外发光改变

Cookie、LocalStorage、SessionStorage区别

  • cookie: 其实最开始是服务器端用于记录用户状态的一种方式,由服务器设置,在客户端存储,然后每次发起同源请求时,发送给服务器端。cookie 最多能存储 4 k 数据,它的生存时间由 expires 属性指定,并且 cookie 只能被同源的页面访问共享。
  • sessionStorage: html5 提供的一种浏览器本地存储的方法,它借鉴了服务器端 session 的概念,代表的是一次会话中所保存的数据。它一般能够存储 5M 或者更大的数据,它在当前窗口关闭后就失效了,并且 sessionStorage 只能被同一个窗口的同源页面所访问共享。
  • localStorage: html5 提供的一种浏览器本地存储的方法,它一般也能够存储 5M 或者更大的数据。它和 sessionStorage 不同的是,除非手动删除它,否则它不会失效,并且 localStorage 也只能被同源页面所访问共享。
  • 上面几种方式都是存储少量数据的时候的存储方式,当需要在本地存储大量数据的时候,我们可以使用浏览器的 indexDB 这是浏览器提供的一种本地的数据库存储机制。它不是关系型数据库,它内部采用对象仓库的形式存储数据,它更接近 NoSQL 数据库。

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

  • Name:cookie的名称
  • Value:cookie的值,对于认证cookie,value值包括web服务器所提供的访问令牌;
  • Size: cookie的大小
  • Path:可以访问此 cookie 的页面路径。 比如 domain 是 abc.com,path是/test,那么只有/test路径下的页面可以读取此cookie。
  • Secure: 指定是否使用HTTPS安全协议发送 Cookie。使用 HTTPS 安全协议,可以保护Cookie在浏览器和Web服务器间的传输过程中不被窃取和篡改。该方法也可用于Web站点的身份鉴别,即在HTTPS的连接建立阶段,浏览器会检查Web网站的SSL证书的有效性。但是基于兼容性的原因(比如有些网站使用自签署的证书)在检测到SSL证书无效时,浏览器并不会立即终止用户的连接请求,而是显示安全风险信息,用户仍可以选择继续访问该站点。
  • 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失效。
  • 总结: 服务器端可以使用 Set-Cookie 的响应头部来配置 cookie 信息。一条cookie 包括了5个属性值 expires、domain、path、secure、HttpOnly。其中 expires 指定了 cookie 失效的时间,domain 是域名、path是路径,domain 和 path 一起限制了 cookie 能够被哪些 url 访问。secure 规定了 cookie 只能在确保安全的情况下传输,HttpOnly 规定了这个 cookie 只能被服务器访问,不能使用 js 脚本访问。

什么是同源策略

  • 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

    • 它的核心就在于它认为自任何站点装载的信赖内容是不安全的。当被浏览器半信半疑的脚本运行在沙箱时,它们应该只被允许访问来自同一站点的资源,而不是那些来自其它站点可能怀有恶意的资源。
    • 所谓同源是指:域名、协议、端口相同。
  • 另外,同源策略又分为以下两种:

    • DOM 同源策略:禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。
    • XMLHttpRequest 同源策略:禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。

如何解决跨越问题

  • CORS。跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain)上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域HTTP 请求。
  • JSONP。jsonp的原理就是利用 <script>标签没有跨域限制,通过<script>标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。
  • postMessage 跨域。postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,比如页面与嵌套的iframe消息传递。
  • nginx代理跨域。 nginx代理跨域,实质和CORS跨域原理一样,通过配置文件设置请求响应头Access-Control-Allow-Origin…等字段。
  • document.domain + iframe跨域。此方案仅限主域相同,子域不同的跨域应用场景。实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

简单请求和复杂请求?

  • 简单请求

    • 简单请求不会触发CORS预检请求。

    • 若该请求满足以下两个条件,就可以看作是简单请求:

      • 1)请求方法是以下三种方法之一:

        • HEAD
        • GET
        • POST
      • 2)HTTP的头信息不超出以下几种字段:

        • Accept
        • Accept-Language
        • Content-Language
        • Last-Event-ID
        • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
    • 简单请求过程

      • 对于简单请求,浏览器会直接发出CORS请求,它会在请求的头信息中增加一个Orign字段,该字段用来说明本次请求来自哪个源(协议+端口+域名),服务器会根据这个值来决定是否同意这次请求。

      • 如果Orign指定的域名在许可范围之内,服务器返回的响应就会多出以下信息头:

        • Access-Control-Allow-Origin: api.bob.com // 和Orign一致
        • Access-Control-Allow-Credentials: true // 表示是否允许发送Cookie
        • Access-Control-Expose-Headers: FooBar // 指定返回其他字段的值
        • Content-Type: text/html; charset=utf-8 // 表示文档类型
      • 如果Orign指定的域名不在许可范围之内,服务器会返回一个正常的HTTP回应,浏览器发现没有上面的Access-Control-Allow-Origin头部信息,就知道出错了。这个错误无法通过状态码识别,因为返回的状态码可能是200。

  • 复杂请求

    • 非简单请求是对服务器有特殊要求的请求,比如请求方法为DELETE或者PUT等。非简单请求的CORS请求会在正式通信之前进行一次HTTP查询请求,称为预检请求。

      • 浏览器会询问服务器,当前所在的网页是否在服务器允许访问的范围内,以及可以使用哪些HTTP请求方式和头信息字段,只有得到肯定的回复,才会进行正式的HTTP请求,否则就会报错。

      • 预检请求使用的请求方法是OPTIONS,表示这个请求是来询问的。他的头信息中的关键字段是Orign,表示请求来自哪个源。除此之外,头信息中还包括两个字段:

        • Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法。
        • Access-Control-Request-Headers: 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。
      • 服务器在收到浏览器的预检请求之后,会根据头信息的三个字段来进行判断,如果返回的头信息在中有Access-Control-Allow-Origin这个字段就是允许跨域请求,如果没有,就是不同意这个预检请求,就会报错。

      • 服务器返回的 CORS

        Access-Control-Allow-Origin: api.bob.com // 允许跨域的源地址

        Access-Control-Allow-Methods: GET, POST, PUT // 服务器支持的所有跨域请求的方法

        Access-Control-Allow-Headers: X-Custom-Header // 服务器支持的所有头信息字段

        Access-Control-Allow-Credentials: true // 表示是否允许发送

        Cookie Access-Control-Max-Age: 1728000 // 用来指定本次预检请求的有效期,单位为秒

      • 只要服务器通过了预检请求,在以后每次的CORS请求都会自带一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

      • 在非简单请求中,至少需要设置以下字段:

        • 'Access-Control-Allow-Origin'
        • 'Access-Control-Allow-Methods'
        • 'Access-Control-Allow-Headers'
      • 减少OPTIONS请求次数

        • 可以后端在请求的返回头部添加:Access-Control-Max-Age:number。它表示预检请求的返回结果可以被缓存多久,单位是秒。
  • CORS中Cookie相关问题

    • 在请求中设置 withCredentials

      • 默认情况下在跨域请求,浏览器是不带 cookie 的。但是我们可以通过设置 withCredentials 来进行传递 cookie.
    • Access-Control-Allow-Credentials 设置为 true

    • Access-Control-Allow-Origin 设置为非 *

事件模型有哪些?

  • 事件是用户操作网页时发生的交互动作或者网页本身的一些操作,现代浏览器一共有三种事件模型:
  • 原始事件模型:DOM0 级事件模型,这种模型不会传播,所以没有事件流的概念,但是现在有的浏览器支持以冒泡的方式实现,它可以在网页中直接定义监听函数,也可以通过 js 属性来指定监听函数。所有浏览器都兼容这种方式。直接在dom对象上注册事件名称,就是DOM0写法。
  • IE 事件模型,在该事件模型中,一次事件共有两个过程,事件处理阶段和事件冒泡阶段。事件处理阶段会首先执行目标元素绑定的监听事件。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过attachEvent 来添加监听函数,可以添加多个监听函数,会按顺序依次执行。
  • 标准事件模型:DOM2 级事件模型,在该事件模型中,一次事件共有三个过程,第一个过程是事件捕获阶段。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。后面两个阶段和 IE 事件模型的两个阶段相同。这种事件模型,事件绑定的函数是addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。

事件循环

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

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

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