浏览器原理与安全防范
# 跨域
博文参考:ajax 跨域,这应该是最全的解决方案了 (opens new window)
要理解跨域,首先我们需要理解同源策略。
同源是在 1995 年由网景公司提出来的一个策略,这个策略的最初的主要内容是:
A 网页如果设置了 cookie,那么 B 网页不能打开。
我们很容易能够看出这个策略的主要目的是为了安全,现如今所有的浏览器都支持这个策略。
随着互联网的发展,同源策略也越来越严格,只要你的协议,域名和端口有一个不一样,浏览器都会将你的请求视为跨域访问,下面这三种行为都会受到限制:
- 用户信息(
cookie
,localStorage
,indexedDB
) iframe
- 网络请求
当你发一个请求给一个非同源的服务器时,你会得到这条警告信息:
# CORS
CORS全称为Cross-origin resource sharing
,这是一个 W3C 的标准,它允许浏览器向非同源服务器发送请求。
这个主要靠后端开启,但是存在兼容问题。
# JSONP
JSONP全称为JSON with padding
这种方式发起请求会从后端得到“被包裹的 JSON 数据”,大概像这样
callback({ name: 'hax', gender: 'Male' });
其中callback
是用户在发起请求时传过去的参数,我们实际要得到的数据会包裹在函数的参数中,我们可以在本地定义这个函数的具体操作,然后再通过script
标签“云调用”一下这个函数,就可以完成我们的跨域请求啦~~
这种方式的主要原理还是利用了script
标签没有跨域限制的漏洞,
这种方式相比较 CORS 来说兼容性会更好一些。
# 关于资源地址
当请求资源时(css
,js
,img
)并不会触发同源限制,利用这个特性,我们可以:
- 用来统计访问量(PV,UV)。
link
和script
标签可以使用 CDN
# 存储
# cookie
cookie
是一种超小型的文本文件,由网景公司的某个员工发明,是网站为了辨别用户身份而存储在用户本地的非常小的数据(只有 4K)。
cookie
会被设置在HTTP
的header
中,会对请求有性能影响。
cookie
由服务器端生成,存储在客户端,可以设置过期时间。
;
cookie 语法的键值对是用等于号隔开的,这点需要注意。
# cookie 的结构
- name: cookie 的名字
- value: cookie 的值
- Expires/Max-Age: 超时时间
- HttpOnly: 只允许 http 使用,可以防止 XSS
- SameSite: 和防止 CSRF 有关
# localStorage
localStorage
的生命周期为永久,除非用户手动清理。
localStorage
的数据大小有 5M。
# sessionStorage
sessionStorage
与localStorage
的最大区别体现在生命周期上 ,一旦浏览器关闭则被清理。
# indexDB
如果localStorage
存不下的话我们可以上浏览器中的这个数据库。
# 页面加载过程
这块的内容可以用来回答那个被问烂了的的问题
总的来说可以分为资源加载阶段和资源渲染阶段。
这里我们默认资源的类型是HTML。
# 资源加载阶段
- 浏览器根据 DNS 服务器找到服务器的 ip 地址。
- 浏览器向这个 ip 发请求 => 服务器处理并返回请求。
# 资源渲染阶段
这部分就是我们前端要详细回答的阶段了。
# 一、dom 树生成
- 首先浏览器在拿到
HTML
文件前要先在计算机中开辟出内存,同时为HTML
分配一个主线程去一行行解析和执行代码。 - 在主线程解析的过程中,如果遇到了外部静态资源(
link
,script
,img
),此时会开辟一个新的任务队列和新的副线程去加载这些资源文件,而主线程继续向下解析代码。 - 当主线程从上往下执行完后,会生成dom 树。
假如说你现在打开一个网页,在加载的过程中你直接把网线给拔了,有很大几率你会碰到一个没有样式表的网页,因为此时 css 资源已经回不来了。。
因此建议将 css 放在 head 中,提前加载。 :::
# 二、Event Loop 生成 CSSom
主线程解析完后,就会进入Event Loop阶段,当任务队列里的 css 静态资源都加载完后,此时会生成CSSom。
# 三、生成渲染树
当dom 树和CSSom都到齐了之后,此时他们俩会结合生成一颗Render tree。
# 四、计算布局
根据生成的渲染树以及视窗(viewport)大小,计算每个 dom 节点的layout
信息(位置,大小),最终生成分层树
如果由于渲染树中的某些属性的更改,而引发布局的更改,就会引发回流。
影响回流的具体操作
- 添加/删除 元素(display 也算)
- 元素位置发生变化(可以使用
transform
,只会触发复合图层) - 更改 style
- 修改浏览器大小,字体大小
最佳实践:避免回流/读写分离
在布局和绘制阶段,你可以使用will-change
来创建新的图层。
有些浏览器为了进行渲染优化会默认将一些属性提取到新的图层,比如transform,opacity,fixed,z-index等等
# 五、绘制与合成
首先要为每个图层生成一个绘制列表(绘制指令),这些指令交给合成线程后,合成线程会将图层分成图块,并将图块进行光栅化成像素图.
最后合成线程会把绘制指令统一发送给浏览器的进程,浏览器通过调用内部的相关 api 最终会生成出页面.
# 安全
# XSS 跨站请求攻击
XSS全称为Cross Site Scripting
攻击者会想尽一切办法将 script 代码注入到网页中,从而执行自己的攻击操作。
# 存储性 XSS 攻击
案例:2015 喜马拉雅
当时喜马拉雅的技术没有对"专辑名称"这个输入框进行 XSS 过滤,黑客就可以通过这个输入框把恶意代码存储到服务器的数据库里
如果别的用户打开这个专辑,那么这段代码就被注进去了
# 反射型 XSS 攻击
这种攻击的恶意代码写在 url 的参数上,黑客往往会诱导受害者去点击这些链接
# 基于 Dom 的 XSS 攻击
这种攻击一般是由于 html 在传输的过程中就被人给改了,XSS 只是发起攻击的配合手段
# 解决方法
- 尖括号转码或 script 内容过滤
- httpOnly 这样 document.cookie 无法读取
- 开启 CSP 白名单,可以通过 meta 标签或在 http 头部设置
Content-Security-Policy
字段 - 用 react 或 vue,因为有虚拟 dom。
# XSRF 跨站请求伪造
通过模拟登陆状态的用户,窃取受害人相关信息
案例:2007 Gmail
谷歌的某高官曾不小心点了一个恶意链接,点完这个链接后没几天自己的域名被盗了.
原因就是当他点完这个链接后, 其实从黑客的网站上带着他的 cookie 往 Gmail 上提交了一个恶意请求,这个恶意请求 会调用 Gmail 上的一些接口,然后偷偷设置了转发规则,于是邮件就被窃取了
# 解决办法
cookie 上设置 SameSite 属性,最好设置为 lax,这样服务器默认就不会接受第三方的 Cookie 了(但导航到外链依旧可以)
验证请求的来源地址(主要依靠请求头的 referer 和 origin 属性)
重要的接口加短信验证码,生物认证。
# 点击劫持
钓鱼网站中比较常见的攻击手段,网站背后其实有一个透明度为 0的 iframe。
# 解决方案
http 的 header 中设置 X-Frame-Options:SAMEORIGIN
# DDOS
以前流行一个词叫“抓肉鸡”,意思是用病毒去感染用户的终端设备,一旦感染成功,这台设备就成了“肉鸡”,就可以对指定的服务发起DDOS。
这个不太好预防,有一些iass提供商会提供WAF服务。
# 相关面试题
# script 中的 defer 和 async
正常情况下,浏览器解析到 script 标签时,会停止 dom 解析,然后去下载 js 脚本,下载完之后再去执行,这个过程是完全同步的。
如果加了 async,js 脚本在下载的过程中就不会阻塞浏览器解析 dom,等到脚本下载完毕后再去执行。
defer 则会更极端一些,会等浏览器解析完别的标签,最后才去解析 js 脚本。
他们都会并行的去下载 js 脚本。
# link 中的 preload 和 prefetch
正常情况下浏览器要解析到 script 标签时,才会去下载脚本。如果想让浏览器提前下载,就可以用 link 标签的 preload 属性。
<head>
<meta charset="utf-8" />
<title>JS and CSS preload</title>
<!-- preload -->
<link rel="preload" href="style.css" as="style" />
<link rel="preload" href="main.js" as="script" />
<!-- prefetch -->
<link rel="prefetch" href="other.js" as="script" />
</head>
2
3
4
5
6
7
8
9
10
11
prefetch 代表的是预取,优先级很低,只会在浏览器空闲时去加载该资源。所以我们可以预判用户要进入哪个页面,然后去预先加载这个页面的资源。
rel 属性全称 relationship,代表当前文档与被链接资源的关系。
prefetch 和 preload 都会命中强制缓存。
# link 中的 dns-prefetch 和 preconnect
顾名思义,一个可以预先解析 dns,一个可以发起预连接。
<html>
<head>
<link rel="dns-prefetch" href="https://fonts.gstatic.com/" />
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin />
</head>
<body>
<p>hello</p>
</body>
</html>
2
3
4
5
6
7
8
9