浏览器安全
xss攻击
指跨站脚本攻击,在网站注入恶意脚本,使之在用户的浏览器上运行,盗取用户信息。
没有对恶意代码进行过滤,与正常的代码混合在一起了。
可以进行的操作
- 获取页面数据
- 破坏页面结构
- 流量劫持
- dos攻击,发送合理请求,占用服务器资源
攻击类型
- 存储型,恶意脚本会存储在目标服务器上
- 反射型:
- DOM型:通过修改页面的dom结构形成xss
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- ⽤户打开带有恶意代码的 URL。
- ⽤户浏览器接收到响应后解析执⾏,前端 JavaScript 取出 URL 中的恶意代码并执⾏。
- 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者冒充⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作
防御方法
- 使用csp,csp本质是建立一个白名单,告诉浏览器那些外部资源可以加载和执行,防止恶意代码的注入攻击
- 一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的方式
- 浏览器的执行来防御,纯前端的方式,不用服务器端渲染
- 不用服务器端渲染
CSRF攻击
跨站请求伪造攻击,攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
利用cookie会在同源请求中携带,发送给服务器的特点,来实现用户的冒充
- POST 类型的 CSRF 攻击,比如构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
如何防御csrf攻击
在设置 cookie 属性的时候设置 Samesite ,限制 cookie 不能作为被第三方使用
有那些可能引起前端安全的问题
- 跨站脚本(xss):一种代码注入方式
- iframe滥用:ifrmae由第三方提供,默认情况下不受控制
- 跨站请求伪造:
- 恶意第三方库
网络劫持有哪几种,怎么防范
dns劫持
修改本地dns记录,引导用户流量到缓存服务器
http劫持
运营商会修改你的http相应内容
浏览器进程及功能
- 浏览器进程:主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
- 渲染进程:核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
- GPU 进程: UI 界面都选择采用 GPU 来绘制
- 网络进程:主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
- 插件进程:主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。
渲染进程的线程
GUI渲染线程 负责渲染浏览器页面,解析HTML、CSS,构建DOM树、构建CSSOM树、构建渲染树和绘制页面;当界面需要重绘或由于某种操作引发回流时,该线程就会执行。
注意:GUI渲染线程和JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
(2)JS引擎线程 JS引擎线程也称为JS内核,负责处理Javascript脚本程序,解析Javascript脚本,运行代码;JS引擎线程一直等待着任务队列中任务的到来,然后加以处理,一个Tab页中无论什么时候都只有一个JS引擎线程在运行JS程序;
注意:GUI渲染线程与JS引擎线程的互斥关系,所以如果JS执行的时间过长,会造成页面的渲染不连贯,导致页面渲染加载阻塞。
(3)时间触发线程 时间触发线程属于浏览器而不是JS引擎,用来控制事件循环;当JS引擎执行代码块如setTimeOut时(也可是来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件触发线程中;当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理;
注意:由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行);
(4)定时器触发进程 定时器触发进程即setInterval与setTimeout所在线程;浏览器定时计数器并不是由JS引擎计数的,因为JS引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确性;因此使用单独线程来计时并触发定时器,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行,所以定时器中的任务在设定的时间点不一定能够准时执行,定时器只是在指定时间点将任务添加到事件队列中;
注意:W3C在HTML标准中规定,定时器的定时时间不能小于4ms,如果是小于4ms,则默认为4ms。
(5)异步http请求线程
- XMLHttpRequest连接后通过浏览器新开一个线程请求;
- 检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中,等待JS引擎空闲后执行;
-
死锁产生的原因和解决思索- 定义:多个进程运行过程中因争夺资源而造成的一种僵局。
- 原因
- 互斥条件:资源互斥
- 请求和保持条件:不放弃
- 不剥夺:不能被抢
- 环路等待:
- 解决
- 资源一次分配完成(请求保持)
- 一个资源得不到分配,那么也不给他其他
- 可剥夺
- 资源有序分配法(按编号一次分配)
浏览器缓存
位置
- service worker:运行在浏览器背后的独立线程,完成离线缓存,消息推送,网络代理等功能。
- memory cache:包含已经抓取到的资源,关闭页面缓存释放
- disk cache
强缓存与协商缓存
使用强缓存(memory cache disk cache)
若资源有效则直接使用,不再发请求,返回状态码200。
通过两种方式来设
http头信息中的expires(过期时间,通过本地时间来判断)和
cache-conrol(优先级更高,使用相对时间)属性
协商缓存
命中协商缓存,会向服务器发送请求,若未修改则使用,返回状态码304。
通过两种方式来设
http头信息中的Etag(资源文件的标识)
last-modified(服务器,资源最后被修改的时间)/ if-modified-since(,携带上次last,是否过期)
Etag/if-None-Match:与服务器资源是否一致(一致304,不一致200)
1. 当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存。
2. 当f5刷新网页时,跳过强缓存,但是会检查协商缓存。
3. 浏览器地址栏中写入URL,回车 浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。(最快)
浏览器渲染原理
- 解析收到文档,生成dom树
- 解析css,生成cssom树
- 根据dom树和cssom规则树构建render树。
- 根据渲染树进行布局,弄清楚各个节点在渲染页面的位置和大小(回流)
- 绘制阶段,遍历渲染树,调用paint方法,将他们的内容绘制在屏幕上
浏览器渲染优化
- js
- 将js文件放在body最后
- 中间不要放script
- script的引入方式
- script
- 立即停止页面渲染,去加载js,加载完成后立即执行
- async
- 下载完成后,立即异步加载,加载后立即执行,多个async属性不能保证加载顺序
- defer
- 下在完成后,立即异步加载,加载后等dom解析好再执行。多个defer按顺序执行
- script
- css
- 使用css样式
- link
- 派发一个新线程去加载,与此同时gui线程会继续向下渲染代码
- import
- gui暂停渲染,加载资源
- style
- 直接渲染
- link
- 使用css样式
- 减少回流与重绘
- 不用table布局
- 使用css表达式
- 不要频繁操作元素样式
- 比卖你频繁操作dom,创建文档片段,在其上进行所有dom操作,最后一次性加入文档中
- 将dom的多个读操作放一款,得益于浏览器的渲染队列机制
文档的预解析
执行js时,另外一个线程解析剩下的文档,并加载后面需要通过网络加载的资源,这种方式可以使资源并行加载,从而使整体速度更快。只解析外部资源的引用
css如何阻塞文档解析(js中请求css)
js脚本执行时可能有请求样式的信息,此时会延迟js脚本的执行,直至完成cssom的下载构建
优化关键渲染路径的步骤
对关键路径进行分析和特性描述
最大限度减少关键资源的数量
优化关键字节数以缩短下载时间
浏览器本地存储
- cookie
- 大小4kb,
- 每次http请求都会携带
- 无法跨域名
- Name:cookie的名称
- Value:cookie的值,对于认证cookie,value值包括web服务器所提供的访问令牌;
- Size: 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失效。
- localStorage
- 5mb
- 持久存储,
- 金在本地,不随http携带
- 兼容性问题
- sessionStorage
- 窗口关闭就删除
- 金在本地
- indexDB
-
IndexedDB有哪些特点?
IndexedDB 具有以下特点:
- 键值对储存:IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
- 异步:IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
- 支持事务:IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
- 同源限制: IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
- 储存空间大:IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
- 支持二进制储存:IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
同源策略
是浏览器最核心也最基本的安全功能。
不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。
协议、端口号、域名必须相同
解决方法
- jsonp
- 简介:利用script标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的json数据,jsonp一定要有对方服务器支持
- 优点:jsonp兼容性好
- 缺点:
- 只支持get方法,局限性
- 容易遭受xss攻击
- 实现流程
- 声明一个回调函数(show),函数名做参数值,要传递给跨域请求数据的服务器,函数形参要获取目标数据(服务器返回的data)
- 创建一个script标签,把那个跨域的api数据接口地址赋值给script的src,还要在这个地址中向服务器传递该函数名(通过?传参)
- 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和他需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,他准备好的数据是show(‘ 我不爱你 ’)
- 最后服务器把准备的数据通过http协议返回给客户端,客户端再调用执行之前声明的回调函数show,对返回的数据进行操作。
-
// index.html function jsonp({ url, params, callback }) { return new Promise((resolve, reject) => { let script = document.createElement('script') window[callback] = function(data) { resolve(data) document.body.removeChild(script) } params = { ...params, callback } // wd=b&callback=show let arrs = [] for (let key in params) { arrs.push(`${key}=${params[key]}`) } script.src = `${url}?${arrs.join('&')}` document.body.appendChild(script) }) } jsonp({ url: 'http://localhost:3000/say', params: { wd: 'Iloveyou' }, callback: 'show' }).then(data => { console.log(data) })
- cors(跨域资源共享)
- 需要浏览器和后端同时支持
- 分为简单请求和非简单请求
- 简单请求
- 满足以下条件
- 使用下列方法
- head
- get
- post
- Content-Type的值仅限于下列三者之一
- text/plain
- multipart/from-data
- application/x-www-form-urlencoded
- 使用下列方法
- 满足以下条件
- 复杂请求
- 在正式通信前会增加一次查询请求,option方法,来知道服务端是否允许跨域请求
- 实际操作
- 简单请求
- postMessage
- 是H5中的api,允许
- 多窗口之间传递消息
- 页面与嵌套的iframe消息传递
- 页面和其打开的窗口信息传递
- 是H5中的api,允许
- webSocket
- 实现了浏览器和服务器的全双工通信
- node中间代理
- 实现原理:同源策略是浏览器要遵守的标准,服务器向服务器请求则可以跳过
- 接收客户端请求
- 转发给服务器
- 服务器相应数据
- 将响应转发给客户端
- nginx反向代理
- 类似node中间代理,搭建中间服务器,转发请求
正向代理和反向代理的区别
两者都是client-proxy-server的结构,区别在于正向代理proxy是client设置的,用来隐藏client
反向代理中,proxy是server设置,用来隐藏server
事件委托
利用事件冒泡机制,因为事件再冒泡过程中会上传到父节点,父节点可以通过对象获取到目标节点,因此可以把子节点监听函数定义在父结点上,由父节点监听函数统一处理多个子元素的事件
特点
- 减少内存消耗
- 动态绑定事件
局限
事件委托会影响页面性能
同步和异步
同步,当一个进程再遇见需要等待的请求时,一直等下去
异步,先执行其他,当消息返回时再执行
如何实现浏览器内多个标签页之间的通信?
实现多个标签页之间的通信,本质上都是通过中介者模式来实现的。因为标签页之间没有办法直接通信,因此我们可以找一个中介者,让标签页和中介者进行通信,然后让这个中介者来进行消息的转发。通信方法如下:
- 使用 websocket 协议,因为 websocket 协议可以实现服务器推送,所以服务器就可以用来当做这个中介者。标签页通过向服务器发送数据,然后由服务器向其他标签页推送转发。
- 使用 ShareWorker 的方式,shareWorker 会在页面存在的生命周期内创建一个唯一的线程,并且开启多个页面也只会使用同一个线程。这个时候共享线程就可以充当中介者的角色。标签页间通过共享一个线程,然后通过这个共享的线程来实现数据的交换。
- 使用 localStorage 的方式,我们可以在一个标签页对 localStorage 的变化事件进行监听,然后当另一个标签页修改数据的时候,我们就可以通过这个监听事件来获取到数据。这个时候 localStorage 对象就是充当的中介者的角色。
- 使用 postMessage 方法,如果我们能够获得对应标签页的引用,就可以使用postMessage 方法,进行通信
进程与线程
- 进程与进程之间完全隔离,一个进程崩溃不会影响其他进程
- 进程与进程之间需要传递某些数据的话,就需要通过
进程通信管道IPC
来传递 - 当一个进程关闭之后,操作系统会回收该进程的内存空间
- 一个进程中的任意一个线程执行出错,会导致这个进程崩溃
- 同一进程下的线程之间可以直接通信和共享数据
- 一个进程中可以并发多个线程,每个线程并行执行不同的任务
进程通讯(两管,两信,小工,s)
匿名管道
- 半双工,数据只能单向流动,要是想双向流动,必须创建两个管道;
- 匿名管道只适用于具有父子关系的进程间;
命名管道
实现了不存在父子进程间的通信
消息队列
通信不及时
涉及到用户态和内核态之间的数据拷贝开销
共享内存
共享内存可以使得多个进程可以直接读写在同一块内存空间中,这是效率最高的进程间通信方式。