本篇文章主要针对常见的一些计算机网络概念进行梳理和总结,便于今后对于web开发的深入理解。
Network
HTTP协议篇
简介
HTTP是基于TCP/IP协议的应用层协议。它不涉及数据报传输,主要规定了客服端与服务器的通信格式,默认使用80端口。
HTTP/1.0
简介
1996年——发布
1.0版本较0.9版本中只有GET命令的情况,还引入了POST和HEAD命令。
HTTP请求和回应格式,除了包括数据部分还必须包括头信息即HTTP HEADER(用来描述元数据)
请求格式
请求命令+多行头信息
1 | GET / HTTP/1.0 请求命令 |
回应格式
头信息+空行+数据。第一行为协议版本+状态码+状态描述
1 | HTTP/1.0 200 OK 第一行 |
Content-Type 字段
头信息为ASCII码,但数据可以为任意格式。因此需要用**Content-Type
告诉客户端该数据的格式**。
常见的Content-Type
字段值如下:
1 | text/plain |
这些数据类型统称为 MIME type,格式为:一级类型/二级类型。
Content-Encoding 字段
由于发送的数据可以为任意格式,因此可以把数据压缩后再发送。**Content-Encoding
字段说明数据的压缩方式**。常见压缩方法:
1 | Content-Encoding: gzip |
同时客户端在请求时,可以说明自己所接受的压缩方式,使用**字段Accept-Encoding
**。如下:
1 | Accept-Encoding: gzip, deflate |
缺点
HTTP/1.0 主要问题是:每个TCP连接只能发送一个请求。数据发送完毕,链接就关闭了。再请求其它资源,就必须再新建一个TCP连接。而TCP连接需要客户端与服务器三次握手,并且开始时由于slow start,速度很慢,因此新建TCP连接成本很高。
为了解决这个问题,部分浏览器使用了非标准字段Connection
。
1 | Connection: keep-alive |
上面这个字段要求服务器不要关闭TCP连接,以便其它请求复用,该可复用的TCP连接直到客户端或服务器主动关闭,才会断开。但这种方法并不标准。
HTTP/1.1
从1997沿用至201几,直到HTTPS。
持久连接
- 1.1版本最大的变化就是引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,并且不用声明
Connection:keep-alive
。 - 客户端与服务器之间一段时间没有通信,就会主动关闭连接。但规范做法是,客户端在最后一个请求时,发送
Connection: close
,明确要求服务器关闭TCP连接。
注:对于同一个域名,大多数浏览器允许同时建立6个持久连接。
管道机制
在1.0版本中,在同一个TCP连接里,如果客户端要请求两个资源,会先发送A请求,然后等待服务器回应,收到响应之后再发送B请求。
在1.1版本中引入了管道机制(pipelining),其允许浏览器同时发送A请求和B请求,但服务器还是按照顺序,先回应A请求,完成之后再回应B请求。
因此,管道机制使客户端可以在同一个TCP连接中同时发送多个请求。
Content-Length 字段
用于声明本次回应的数据长度。
分块传输编码
简言之,就是服务器产生一块数据就发送一块,即采用流模式(stream)取代缓存模式(buffer)。
因此,1.1版本可以不使用Content-Length
字段,而使用分块传输编码(chunked transfer encoding)。只要请求或回应的头信息中有Transfer-Encoding
字段,就表明回应将由数量未定的数据块组成。
注:每个非空的数据块之前,会有一个16进制的数值来表示这个块的长度。最后是一个大小为0的块,表示本次回应的数据发送完毕。示例如下:
1 | HTTP/1.1 200 OK |
其它功能
- 1.1版本还新增了许多动词方法,如:
PUT
,PATCH
,HEAD
,OPTIONS
,DELETE
- 客户端请求头信息新增了
HOST
字段,用来指定服务器的域名。有了HOST
字段就可以将请求发往同一服务器上的不同网站。(为虚拟机兴起打下基础)
缺点
存在队头堵塞(Head-of-line blocking)。尽管允许复用TCP连接,但在同一个TCP连接里,数据通信依然是按次序进行,服务器只有处理完第一个请求的回应,才能进行下一个请求的回应。如果前一个请求的回应很慢,那么后面就会有许多请求排队。
避免队头堵塞的方法有:
1. **减少请求次数**。例如:合并脚本和样式表、将图片嵌入CSS代码
1. **同时多开持久连接**。例如:域名分片(domain sharding)即所需下载可以来自多个域,可以解决并发限制。
HTTP/2
二进制协议
HTTP/1.1版本的头信息是文本(ASCII码),数据体是文本或二进制。但HTTP/2则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为“帧”(frame):头信息帧和数据帧。
二进制协议的好处是:可以定义额外的帧。
多工 Multiplexing
在HTTP/2中,在同一个TCP连接中,客户端和浏览器都可以同时发送多个请求和响应,并且不用按照顺序一一对应,这就避免了队头堵塞的问题。
例如:在一个TCP连接中,服务器同时收到A请求和B请求,于是先回应A请求,结果发现处理十分耗时,于是就发送A请求已处理好的部分,接着处理回应B请求,完成之后,再处理发送A请求的剩下的部分。我们称这种双工的、实时的通信为多工。
数据流
HTTP/2的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应,因此需要对数据包进行标记,指出它属于哪个回应。
HTTP/2将每个请求或者回应的所有数据包都称为一个数据流(stream)。每个数据流都有一个唯一的编号。数据包发送的时候,必须标记数据流ID,一标识它属于哪个数据流。此外客户端发出的数据流ID一律为奇数,服务端发出的数据流ID一律为偶数。
数据流发送到一半时,客户端和服务器都可以发出信号来取消这个数据流。在HTTP/1.1版本中,取消数据流发送的唯一方法是关闭TCP连接。HTTP/2可以取消某一次请求同时保证TCP连接还是开启的状态,可以被其它请求复用。
客户端还可以指定数据流的优先级,优先级越高,服务器会越早对其回应。
头信息压缩
由于每次请求,很多请求头信息中的字段都是重复的(如Cookie、User Agent等),这会造成很多带宽的浪费。HTTP/2优化了这一点,引入头信息压缩机制。一方面,头信息使用gzip
等方法压缩后再发送;另一方面,客户端和服务端维护一张头信息表,该表为字段和索引号的映射,之后就只发送索引号,以提高速度。
服务器推送
HTTP/2允许服务器未收到请求,主动向客户端发送资源。
例如:客户端请求一个包含很多静态资源的网页,服务器会主动把这些静态资源随网页一起发送给客户端,这样就不需要等客户端收到网页后解析HTML发现有静态资源再请求静态资源了。
HTTPS——番外篇
互联网的通信安全是建立在SSL/TLS协议之上的。
注:SSL——Secure Sockets Layer, TLS——SSL的升级
使用HTTPS的原因&作用
对比:
HTTP不使用SSL/TLS,因此是不加密的通信,所有信息都是明文传播
- 窃听风险(eavesdropping):第三方可以截获并获知通信内容
- 篡改风险(tampering):第三方可以截获并修改通信内容
- 冒充风险(pretending):第三方可以冒充他人身份参与通信
基于SSL/TLS协议的HTTPS的好处
- 所有信息都是加密传输——无法被窃听
- 具有校验机制——通信内容一旦被篡改,通信双方都能发现
- 配备身份证书——防止身份冒充
SSL/TLS协议的基本运行过程
该协议的基本思路为:非对称加密:公钥加密,私钥解密。即客户端先向服务端索要公钥,然后再用公钥加密信息,服务器收到密文后,用自己的私钥解密。
保证公钥不被篡改的方法
答:将公钥放入数字证书中,只要证书是可信的,那么公钥就是可信的。
用公钥加密计算量太大,需要减少耗用时间的方法
答:每一次对话(session),客户端和服务端会生成一个“对话密钥”(session key),用它来加密信息。由于“对话密钥”是对称加密,因此运算速度非常快,因此如果服务器公钥只用于加密“对话密钥”本身的话,就会大大减少加密运算的消耗时间。
所以,SSL/TLS协议的基本过程是:
- 客户端向服务端索要并验证公钥 —— 握手阶段
- 双方协商生成“对话密钥” —— 握手阶段
- 双方采用“对话密钥”进行加密通信
握手阶段
握手阶段涉及四次通信,该阶段的所有通信都是明文的。
阶段一:客户端发出请求
首先,客户端向服务器发送加密通信请求。其发送的请求内容主要有以下:
- 支持的协议版本,例如:TLS 1.0
- 客户端生成的随机数,用于稍后生成“对话密钥”
- 支持的加密方法,例如:RSA公钥加密
- 支持的压缩方法
阶段二:服务器回应
服务器收到客户端请求后,向客户端发出响应。响应内容主要包括以下:
- 确认使用的加密通信协议版本。如果双方支持的版本不一致,则服务器会关闭本次加密通信
- 服务器生成的随机数,用于稍后生成“对话密钥”
- 确认使用的加密方法
- 服务器证书
阶段三:客户端回应
客户端收到服务器的响应后,首先要验证服务器的数字证书。
如果1. 证书不可信。2. 证书中的域名与实际域名不一致。3. 证书已经过期,就会向访问者显示一个警告,让其选择是否继续通信。
如果证书没有问题,客户端会从证书中取出服务器的公钥,然后向服务器发送响应,其内容主要如下:
- 客户端生成的随机数。该随机数用公钥加密,防止被窃听。
- 编码改变通知。表示随后的信息将用双方商定的加密方法和密钥发送。
- 客户端握手结束通知。表示客户端的握手阶段结束,这一项也是前面发送的所有内容的hash值,用来供服务器校验。
注:这个阶段生成的随机数是整个握手阶段出现的第三个随机数(”pre-master key”)。有了它之后,客户端和服务器就都同时有了三个随机数,然后双方用事先商定的加密方法,各自生成本次会话所用的同一把“会话密钥”。
阶段四:服务器最后回应
服务器收到第三个随机数之后,会计算生成本次会话所用的“会话密钥”。然后向客户端发送最后响应。其内容主要如下:
- 编码改变通知。表示随后的信息将用双方商定的加密方法和密钥发送。
- 服务器握手结束通知。表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。
至此,整个握手阶段全部结束。接下来就是,客户端与服务器进行加密通信,这就完全是使用普通的HTTP协议了,只不过所发送的内容都使用“会话密钥”进行了加密。
网络安全篇
XSS——跨站脚本
介绍
XSS,全称为Cross-site scripting,中文名称为跨站脚本,是一种对网络应用程序的安全漏洞的攻击,即是一种代码注入。这类攻击通常包含了HTML和用户脚本语言(主要为JavaScript)。
例如,用JavaScript在网站论坛上发布一段恶意的JavaScript代码,这就是脚本注入,而同时如果这个代码内容有请求外部服务器的功能,那么就叫做XSS。
因此,可以理解为XSS(跨站脚本)= 脚本注入 + 请求外部服务器的功能。
示例
例如,在某论坛网站中的发帖内容写下如下代码:
1 | <script> |
假设该论坛网站没有任何过滤及其它防御机制的情况下,任何访问这个帖子的用户,都会在客户端界面一直弹出这个弹窗。
这就是最简单的脚本注入。当然这个脚本并没有什么实质性的危害,那么XSS就是基于脚本注入的方法来完成,但它注入的是含有请求跨站功能的脚本。
例如,在上面提到的论坛网站中,注入如下代码:
1 | <script> |
该段代码会将任何访问该帖子的用户的cookie传给一个为http://192.168.123.123/myxss/
的服务器,该服务器可以拿着这个cookie访问对应的网站,并登录该用户的账号,继而可以进行其它操作。
因此,上面这段代码就是XSS。
CSRF——跨站请求伪造
介绍
CSRF,全称为Cross-site request forgery,中文名为跨站请求伪造。是一种让用户在当前已登录的Web应用程序上执行一些非本意的操作(如改名,删帖,发邮件等)的攻击方法。
攻击原理
一般来说,CSRF是由XSS实现的,在跨站脚本的基础上,实现伪造请求,导致受害者会执行一些自己本不愿意执行的操作。
注:CSRF也可以不通过XSS来实现。
示例
还是以上面的对某论坛网站的XSS攻击为例,在外部服务器成功获得用户的cookie后,它可以利用cookie,伪造成用户对论坛网站发起一系列的请求,例如修改用户名称,修改用户密码等。此时,就可以称该攻击为CSRF。
防范XSS和CSRF⭐️
核心思想:不相信任何外部来源数据!!!
一般来说,大部分的CSRF都是基于XSS的,因此可以说防住了XSS,也就基本防住了CSRF。
对于防范XSS,主要有以下两个方法:
- 输入过滤:对外部输入进行彻底的敏感字符过滤——前端+后端
- 输出过滤:在显示在页面上时,做一些处理让敏感的代码脚本无法顺利执行——前端
理论上,只要有输入数据的的地方,就存在XSS的漏洞,JavaScript的脚本可以以各种形式(如明文、编码等)注入到数据库中。
就像上面提到的 JS 会以各种非法形式注入,其主要非法形式有两类:
明文:
1
2
3<script>
... // js 恶意代码
</script> 要处理好明文注入的过滤。
编码:
1
u0026u006cu0074u003bu0073u0063u0072u0069u0070u0074u0026u0067u0074u003bu0061u006cu0065u0072u0074u0028u0026u0023u0033u0039u003bu6211u662fu0078u0073u0073uff0cu4f60u6709u9ebbu70e6u4e86u0026u0023u0033u0039u003bu0029u0026u006cu0074u003bu002fu0073u0063u0072u0069u0070u0074u0026u0067u0074u003b
上面这个编码为unicode编码,它可以绕过
htmlSpecialChars
的过滤并入库,然后在展示该信息的时候,html 会自动将unicode编码转换成明文即真正的可执行脚本。由于不仅unicode编码可以注入,其它类型的编码也可以(道理类似),我们需要尽量将页面的字符编码统一设置为一种(如unicode: utf-8),这样便于我们专注于处理unicode编码的注入。
其它一些通用的防范方法
在输出html时,加上
Content Security Policy
的Http Header
作用:防止页面被XSS攻击时,嵌入第三方的脚本文件
在设置Cookie时,加上
HttpOnly
参数作用:可以防止页面被XSS攻击时,Cookie信息被盗取
缺点:网站本身的
JavaScript
代码也无法操作Cookie在开发API时,检验请求的Referer参数
作用:可以在一定程度上防止CSRF攻击