【Web安全/操作系统/性能优化】

824 阅读19分钟

Web安全

Web安全

私密性:不被非法获取,盗用。
可靠性:内容不会被修改、丢失或额外注入。

攻击类型

1.CSRF跨域请求伪造

CSRF攻击

CSRF指的是跨站请求伪造,攻击者利用相关网站对用户身份的信任来窃取用户信息甚至涉及用户安全。

例如用户登录网站A,由于已通过网站A的用户身份验证,因此在没退出的情况下可访问,当用户无意通过恶意网站再次访问网站A时,恶意网站会欺骗网站A,让其误以为是用户本意登录,实质上是恶意网站的一种欺骗行为。

CSRF防范

  • 验证refener信息,查看登录来源,但这种方式容易被篡改。
  • 采用token验证身份信息。服务器端会向客户端返回一个随机数,作为身份令牌,当再次访问时客户端将这个随机数存请求参数中,服务器端会检验这个身份令牌是否符合,但这种方式的缺点是我们需要为每个服务端都储存一个token,这样会比较繁琐,同时一般不会只对应一个服务器当我们进行负载平衡时,请求会被转移到其他服务器上,这样就要重新生成token。
  • 双重cookies方式,服务器端会将cookies返回给客户端,当客户端再次发起请求时,会将cookies的信息存放在url参数中,服务器会将cookies中的数据和url中的参数进行对比,来判断是否请求来自于恶意网站,这是利用了攻击者只能访问cookies而不能获取cookies的特点。
  • 可以对cookies设置Samesite属性,限制cookies不能被第三方所获取。
    1. Samesite Cookies表示同站Cookies,表示cookies不能被第三方所获取。
      1. 将Samesite设置为strict时,表示严格模式下,cookis不能被任何第三方所利用。
      2. 将Samesite设置为lax时,表示款式模式下,当请求为get请求时,并且这个请求改变了当前页面或者打开了新的页面,则cookies可以被第三方获取。
    2. 这种方式的缺点是它不支持子域,当从主域跳转到子域时,用户需要重新登录,且兼容性不够好。

2.XSS跨站脚本攻击

XSS指的是跨站脚本攻击,恶意攻击者会通过某种方式对页面中注入额外JS代码,对页面进行修改或是窃取cookies,影响用户体验以及造成用户信息的不安全。

XSS攻击

  1. 一种是反射型,攻击者可将恶意代码加载url地址中,当用户操作页面向服务器提交url时,服务器会提取url中的参数包括了恶意代码,并传递给客户端执行时会就执行那些恶意代码,这是反射型。
  2. 一种是储存型,攻击者将恶意代码存放在数据库中,当用户将从数据库中获取数据时会获取到那些恶意代码,然后传到浏览器端执行。

XSS防范

XSS防御可分为在浏览器端向服务器端请求阶段的防御浏览器端执行代码时的防御。

  • 浏览器端提交代码时:可对代码进行转义处理,但不同的代码提交位置不同因此将所有js代码进行转义处理不是好的方法。
  • 浏览器端执行代码时:可为http头部设置httponly属性,意味着cookies不能通过js脚本获取,这样保证了cookies的安全性。
  • 还可以使用验证码,防止攻击者伪装成用户进行操作。
  • 还可以使用CSP,为浏览器建立一个白名单,通过白名单告知浏览器哪些资源可以得到访问,防止恶意代码的访问和攻击。 CSP为内容安全策略,两种方式进行设置
    1. 第一种为http头部设置“Content-Security-Policy"
    2. 另一种为meta标签内设置http-equiv="Content-Security-Policy"。

3.SQL注入

SQL是一种结构化查询语言,是一种特殊目的的编程语言,是一种数据库查询和结构设计语言,用于存储数据以及查询、添加、修改和删除数据的操作关系型数据库的语言。

SQL注入攻击

SQL注入是攻击者通过用户在进行http请求的过程中注入恶意的SQL代码,当服务器在使用参数构建数据库
SQL命令时,恶意的SQL代码也会被一起构建,并在数据库中执行产生意料之外的结果的攻击行为。

SQL注入防御

永远不要使用动态的方式拼接sql,并不轻易信任用户的输入,不把机密信息直接存放。

4.点击劫持

点击劫持攻击

是一种视觉欺骗的手段,指的是攻击者将恶意网站通过iframe放置于页面当中,并且将其外观设置为透明,在底部创建一个按钮之类的引诱用户去点击,随后跳转到恶意网站。

点击劫持防范

  1. 使用X-FRAME-OPTIONS机制
    1. DENY表示任何网页都不能使用iframe载入网页
    2. SOMEORIGIN表示符合同源策略的网站可以使用iframe载入网页,如果浏览器使用了这个机制,当用户访问网站时会提示当前网页存在安全隐患,应打开新网页去浏览。
    3. ALLOW-FROM,表示页面可以在指定来源的iframe中展示
  2. 通过JS防御

5.中间人攻击

中间人攻击

是攻击人同时与客户端和浏览器端建立联系,并且让对方认为连接是安全可靠的,但实际上整个连接都被攻击者控制,攻击者可以任意获取连接中的信息并改写信息。通常来说不建议使用公共WiFi,因为很可能就会发生中间人攻击的情况,如果在通信的过程中涉及到了某些私密信息就很有可能会被窃取。

中间人攻击防范

可以通过使用https协议。

操作系统

进程与线程

进程:

进程是资源分配的基本单位,用来管理资源(例如:内存、文件、网络等资源)

进程控制块(PCB)

描述进程的基本信息和运行状态,所谓的创建进程和撤销进程都是对进程控制块的操作。PCB是描述进程的数据结构。

线程:

线程是独立调度的基本单位。一个进程中可以有多个线程,它们共享进程资源。

例如QQ和浏览器是两个进程,浏览器进程中里面有很多线程,例如http请求线程,事件响应线程,渲染线程等等。

线程的并发执行使得在浏览器中点击一个新链接从而发起http请求时浏览器还可以响应用户的其他事件。

进程与线程的区别

  • 拥有资源:进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源。
  • 调度:线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换。
  • 系统开销:由于创建和撤销进程时,系统都要为之分配或回收资源,如内存空间等,所付出的开销远大于创建或撤销线程时的开销,因此线程切换时开销较小。
  • 通信方面:进程间通信需要进程同步和互斥手段的辅助,以保证数据的一致性,而线程间可以通过直接读写同一进程中的数据段来进行通信。

死锁

  • 死锁概念和产生原因:

    1. 死锁是指多个进程循环等待对方所占有的资源而出现的僵持等待的局面。
    2. 产生原因是系统提供的资源太少远不能满足并发进程对资源的需求;以及进程推进顺序不合理,互相占有彼此所需要的资源,同时又请求对方所占有的资源,形成死锁。
  • 死锁产生的必要条件

    1. 互斥条件:某个资源在某一时刻只能被一个进程所占有执行,而不能同时被两个或两个以上的进程占有。
    2. 不可抢占条件:进程所获得的资源在使用完成之前,其他资源申请的进程不能强行从资源占有者手中夺取资源,而只能由占有进程自行释放才可。
    3. 占有且等待条件:进程至少已经占有了一个资源,但又申请了一个新的被其他进程所占有的资源,此时处于等待状态。
    4. 循环等待条件:若干个进程形成环形链,每个都占有对方所申请的下一个资源。
  • 死锁的处理策略

    1. 为了不让系统方法死锁现在,必须破坏产生死锁的四个条件之一,或是可以检测并有能力实现恢复。

性能优化

1. CDN的缓存与回源机制解析

CDN指的是将源网站的资源缓存到分别在不同地理位置的服务器上,这些服务器存储着数据的副本,因此服务器可以根据哪些服务器与用户距离最近来满足数据的获取。CDN提供快速服务,较少受高流量影响。

CDN往往被用来存放静态资源。静态资源本身具有访问频率高,承接流量大的特点,因此静态资源加载速度始终是前端性能的一个非常关键的指标。
首先CDN服务器域名和业务服务器域名不一致。因为同一域名下的所有请求都会携带Cookies,但若我们只想请求一张图片或一个CSS文件,此时每次携带cookies开销会非常大,并且cookies中储存的信息目前并不需要,因此把静态资源和主页面置于不同的域名下,完美地避免了不必要的cookies的出现。

2. 前端需要注意哪些SEO

  • 合理的title,description,keywords:搜索对着三项的权重逐渐减小,title值强调重点即可,重要关键词出现不要超过两次,而且要靠前。description把页面的内容高度概括,长度合适,不可过分堆砌关键词,不同页面的description有所不同。keywords列举出重要关键词即可。
  • 语义化的html代码,让搜索引擎容易理解网页。
  • 重要内容html代码放在最前面,因为搜索引擎抓取html顺序是从上至下的,有的搜索引擎对抓取长度有限制,保证重要内容一定会被抓取。
  • 重要内容不要用js输出,因为爬虫不会执行js获取内容。
  • 少用iframe,搜索引擎不会抓取iframe中的内容
  • 非装饰性图片必须加alt。
  • 提高网站速度:网站速度是搜索引擎排序的一个重要指标。

3. webpack中的优化

有哪些方式可以减少webpack的打包时间?

1. 优化Loader

对于Loader来说,影响打包效率首当齐冲必属Babel了。因为babel会将代码转为字符串生成AST,然后对AST继续进行转变最后生成新的代码,项目越大,转化代码越多,效率就越低。当然了,我们是有办法优化的。

module.exports={
	module:[
		rules:[
			{
				//js文件才使用babel
				test:'\.js$',
				loader:'babel-loader',
				//只在src文件夹下查找
				include:[resolve('src')],
				//不会去查找路径
				exclude:/node_modules/
			}
		]
	}
}

对于Babel来说,我们肯定是希望只作用在JS代码上的,然后node_modules中使用的代码都是编译过的,所以我们也完全没有必要再去处理一遍… 当然这样做还不够,我们还可以将Babel编译过的文件缓存起来,下次只需要编译更改过的代码文件即可,这样我们可以大幅度加快打包时间。

loader:'babel-loader?cacheDirectory=true'

2. HappyPack

受限于Node是单线程运行的,所以Webpack在打包的过程中也是单线程的,特别是在执行Loader的时候,长时间编译的任务很多,这样就会导致等待的情况。

HappyPack可以将Loader的同步执行转换为并行的,这样就能充分利用系统资源来加快打包效率了。

module:{
	loaders:[
		{
			test:/\.js$/,
			include:[resolve('src')],
			exclude:/node_modules/,
			//id后面的内容下面
			loader:'happypack/loader?id=happybabel'
		}	
	]
},
plugins:[
	new HappyPack({
		id:'Happybabel',
		loaders:['babel-loader?cacheDirectory'],
		//开启4个线程
		threads:4	
	})
]

3. DllPlugin

DllPlugin可以将特定的类库提前打包然后引入。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。

//单独配置在一个文件中
const path=require('path');
const webpack=require('webpack');
module.export={
	entry:{
		//统一打包类库
		vendor:['react'];	
	},
	output:{
		path:path.join(__dirname,'dist'),
		filename:'[name].dll.js',
		library:'[name]-[hash]'	
	},
	plugins:[
		new webpack.Dllplugin({
			name:'[name]-[hash]',
			context:__dirname,
			path:path.join(__dirname,'dist','[name]-manifest.json')	
		})	
	]
}

然后我们需要指向这个配置文件生成依赖文件,接下来我们需要使用DllReferencePlugin将依赖文件引入项目中

//webpack.conf.js
module.exports={
	plugins:[
		new webpack.DllRefercePlugin({
			context:__dirname,
			manifest:require('./dist/vendor-manifest.json'),
		})
	]
}

4. 代码压缩

在webpack3中,我们一般使用UglifyJS来压缩代码,但是这个是单线程运行的,为了加快效率,我们可以使用webpack-parallel-uglify-plugin来运行UglifyJS,从而提交效率。

在webpack4中,我们就不需要以上操作了,只需要将mode设置为production就可以默认开启以上功能。代码压缩也是我们必做的性能优化方案,当然我们不知可以压缩JS代码,还可以压缩HTML、CSS代码,并且在压缩JS代码的过程中,我们还可以通过配置实现比如删除console.log这类代码的功能。

5. 一些小的优化点

  • reslove.extensions:用来表明文件后缀名列表,默认查找顺序是[’.js’,’.json’],如果你的导入文件没有添加后缀就会按照这个顺序查找文件。我们应该尽可能减少后缀列表长度,然后将出现频率高的后缀排在前面。
  • resolve.alias:可以通过别名的方式来映射一个路径,能让webpack更快找到路径
  • module.noParse:如果你确定一个文件下没有其他依赖,就可以使用该属性让webpack不扫描该文件,这种方式对于大型类库很有帮助

有哪些方式可以让webpack打出来的包更小?

1. 按需加载

相比大家在开发SPA项目的时候,项目中都会在十几甚至更多的路由页面。如果我们将这些页面全部打包进入一个JS文件的话,虽然将多个请求合并了,但是同样也加载了很多并不需要的代码,耗费了更长的时间。
那么为了首页能更快地呈现给用户,我们肯定希望首页能加载文件体积越小越好,这时候我们就可以使用按序加载,将每个路由页面单独为一个文件,当然不仅仅路由可以按需加载,对于loadash这种大型类库同样可以使用这个功能。按需加载的底层机制都是当使用的时候再去下载的对应文件,返回一个Promise,当Promise成功以后去执行回调。、

2. Scope Hoisting

Scope Hoisting会分析出模块之间的依赖关系,尽可能的把打包出来的模块合并到一个函数中去。 比如我们希望打包两个文件

//test.js
export const a=1
//index.js
import {a} from './text.js'
1234

对于这种情况,我们打包出来的代码会类似这样

[
	/*0*/
	function(module,exports,require){
		//...	
	}
	/*1*/
	function (module,exports,require){
		//...	
	}
]
12345678910

但是如果我们使用Scope Hoisting的话,代码就会尽可能的合并到一个函数中去,也就变成了这样的类似代码

[
	/*0*/
	function (module,exports,require){
		//...
	}
]
123456

这样的打包方式生成的代码明显比之前少很多,如果在webpack4中你希望开启这个功能,只需要启用optimization.concatenateModules就可以了。

module.exports={
	optimaization:{
		concatenateModules:true
	}
}
12345

3. Tree Shaking

Tree Shaking可以实现删除项目中未被引用的代码,比如:

//test.js
export const a=1
export const b=2
//index.js
import {a} from './test.js'
12345

对于以上情况,test文件中的变量b如果没有在项目中使用到的话,就不会被打包到文件中。如果你使用webpack4的话,开启生产环境就会自动启动这个优化功能。

4. 编写高性能的JavaScript

1.为什么要将js放在body尾部?
  1. JS引擎是独立于渲染引擎存在的。js代码在文档的何处插入,就在何处执行。当html解析器遇到一个script标签时,就会暂停渲染,将控制权交给js引擎,js引擎对内联的js代码会直接执行,对外部js文件还要先获取到脚本再进行执行。等js引擎运行完毕浏览器又会把控制权还给渲染引擎,继续CSSOM和DOM的构建。
  2. 如果js放在header中,浏览器会阻塞并等待js加载完毕并执行。
  3. 如果js在body尾部,浏览器会进行一次提前渲染,从而提前首屏出现时间。
2. 非核心代码的异步加载?
  1. 动态脚本加载:使用js创建一个script标签然后插入到页面中
  2. defer属性:整个html解析完之后才执行,如果是多个就按照顺序执行
  3. async属性:加载完之后立即执行,如果是多个,执行和加载顺序无关。
3. 使用节流与防抖
4. 懒加载

5. 浏览器渲染

1.浏览器的解析过程:

  • 解析HTML构建DOM树,并行请求CSS/image/js
  • CSS文件下载完毕,开始构建CSSOM树
  • CSSOM树构建完成后,和DOM一起生成渲染树
  • 布局(layout):计算出每个节点在屏幕中的位置
  • 显示(painting):页面渲染到屏幕上

2.DOM树和渲染树的区别:

DOM树与HTML标签是一一对应的,包括head和隐藏标签
渲染树不包括head和隐藏元素,每个节点都有对应的css属性

3.css会阻塞DOM解析吗?

对于一个HTML文档来说,不管是内联还是外联的CSS,都会阻碍后序的DOM渲染,但不会阻塞后序的DOM解析。

当css文件放在head标签中时,虽然css解析也会阻塞后序dom的渲染,但是在解析css的同时也在解析dom,所以等到css解析完毕就会逐步的渲染页面了。

4.重绘和回流(重排)的区别和关系?

重绘:当渲染树中的元素外观(颜色)发生改变时,不影响布局时产生重绘
回流:当渲染树中的元素的布局(尺寸,位置,隐藏\状态改变)发生改变时产生回流

回流一定引起重绘,而重绘不一定引起回流

什么时候回触发回流:DOM结构中的各元素都有自己的盒子,这些都需要浏览器根据各种样式来计算并根据结果将它们放置在页面的对应位置上 这个过程叫回流

  • 添加或删除可见的DOM元素
  • 元素位置改变
  • 元素的尺寸改变(包括内外边距、边框厚度、宽度、高度等属性的改变)
  • 内容改变
  • 页面渲染器初始化
  • 浏览器窗口尺寸改变

注意:JS获取布局属性值(例如:offsetLeft、scrollTop、getComputedStyle等)也会引起回流,因为浏览器需要通过回流计算最新值

5.如何最小化重绘和回流?

  1. 需要对DOM元素进行复杂的操作时,可以先隐藏(display:none),操作完成后再显示
  2. 需要创建多个DOM节点时,使用DocumentFragment创建完成之后将节点不断的添加到DocumentFragment中,然后一次性的加入到documenet,或者使用字符串拼接方式构建好对应html后再使用innerHTML来修改页面
  3. 将布局属性赋值给变量,这样不用每次需要的时候都重新计算一次布局属性,例如将var left = elem.offsetLeft,这样多次使用left只会产生一次回流
  4. 避免使用table布局,因为table元素一旦触发回流就会导致table里所有的其他元素回流
  5. 避免使用css表达式,因为每次调用都会重新计算值,包括加载页面
  6. 尽量使用css属性简写 用border代替border-width border-style border-color
  7. 批量修改样式 例如用elem.className代替elem.style.xxx

6. 雅虎军规

网络部分:

  • 尽量减少HTTP请求数:合并文件、雪碧图、使用小图base64
  • 减少DNS查询:开启DNS预解析。html源码在下载完成后,会解析页面的包含链接的标签,提前查询对应的域名。对于访问过的页面,浏览器会记录一份域名列表,当再次打开时,会在html下载的同时去解析DNS
  • 使用CDN内容分发网络静态资源服务器
  • 避免重定向
  • 杜绝404状态码

缓存:

  • 配置ETag,是服务器和浏览器用来决定浏览器缓存中组件与源服务器中的组件是否匹配的一种机制。
  • 添上Expires和Cache-Control HTTP头部
  • 使用外链的方式引入JS和CSS

内容部分

  • 按需加载组件
  • 预加载组件
  • 减少DOM元素的数量
  • 尽量少用iframe
  • 压缩JavaScript和CSS(代码层面)

CSS部分

  • 避免使用CSS表达式
  • 尽量选择link标签
  • 避免使用滤镜
  • 把样式表放在顶部

JS部分

  • 把脚本放在底部
  • 去除重复脚本
  • 减少DOM访问

图片部分

  • 选用合适的图片格式
  • 雪碧图中间少留空白
  • 不要用HTML缩放图片,用小图的时候去加载小图。
  • 用小的可缓存的favicon.ico

cookies

  • 清除不必要的cookies,cookies尽可能的小,设置好合适的有效期。
  • 把静态资源放在不含cookie的域下,当浏览器发送对静态图像的请求时,cookie也会一起发送,而服务器根本不需要这些cookie。

移动端

  • 保证所有组件都小于25k
  • 把组件打包到一个复合文档里

服务器端

  • 开启Gzip压缩
  • 对Ajax用get请求
  • 尽早清空缓存区
  • 使用CDN(内容分发网络)

后言

希望自己能找到一个好实习,也希望某一点对小伙伴们有所帮助

借鉴文章 - 牛客网