HTTP 详解
1、经典五层模型
传输层: 向用户提供可靠的端到端
服务。传输层向高层屏蔽了下层数据通信的细节
。
应用层: 为应用软件提供服务,构建于TCP协议之上
,屏蔽了网络传输相关细节
。
2、HTTP的三次握手
image image image image.png image.png image.png3、抓包工具分析
image4、HTTP报文格式
image imageGET和POST方式的区别 标准答案:
-
get是获取资源。安全的,幂等的,可缓存的。
-
post是处理资源。非安全的,非幂等的,不可缓存的。
-
安全性的定义:
不应该引起server端的任何状态变化。
get、head、options这几种请求方式都具有安全性。 -
幂等性的定义:
同一个请求方法执行多次和执行一次的效果完全相同。
get方式具有幂等性。
- 可缓存性的定义:
请求是否可以被缓存。
get和head都是可缓存性的。
HTTP的特点:
1)无连接
经过了建立连接和释放连接的过程。可以通过HTTP的持久连接方案来进行无连接的补偿。
2)无状态
对于同一个用户,多次进行http请求时,server是不知道是否是同一个用户。可以通过cookie/session来解决。
非持久连接的定义:
每次进行http请求都是重新创建一个连接,经历三次握手和四次挥手。
持久连接的定义:
打开一条tcp通道,多个http请求在同一条tcp通道上进行,在一段time后关闭。
持久连接:
头部字段:
connection:keep-alive,表示客户端期许采用持久连接。
time:20,持久连接持续多久有效。
max:10,这条连接最多发生多少个http请求。
持久连接中,怎样判断一个请求是否结束?
1)通过响应中的content-length字段的值来判断。
2)chunked,比如通过post请求server端给客户端可能会多次响应返回数据,当有多个块通过http的tcp连接传给客户端时,每一个报文都会带有chunked字段,而最后一个块是一个空的chunked。所以可以通过判断哪个chunked是空的来判断前一个网络请求是否结束。
charles抓包原理是怎样的?
利用了http的中间人攻击这个漏洞。
中间人攻击的定义:
当client发送一个http请求时,是由中间人进行hold住,然后中间人假冒client的身份向server端进行同样的请求,然后server端返回结果给中间人,再由中间人返给client。
如果使用http进行请求或者响应时,中间人可以篡改我们发起的请求参数,server端发回的数据也可以被篡改之后再发给client。
6、状态码的含义:
- 200:请求被正常处理
- 204:请求被受理但没有资源可以返回
- 206:客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过Content-Range指定范围的资源。
- 301:永久性重定向
- 302:临时重定向
- 303:与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上
- 304:发送附带条件的请求时,条件不满足时返回,与重定向无关
- 307:临时重定向,与302类似,只是强制要求使用POST方法
- 400:请求报文语法有误,服务器无法识别
- 401:请求需要认证
- 403:请求的对应资源禁止被访问=
- 404:服务器无法找到对应资源
- 500:服务器内部错误
- 503:服务器正忙
7、跨域问题实战
创建server.js
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html)
}).listen(8888)
console.log('server listening on 8888')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8887/');
xhr.send();
</script>
创建server2.js
const http = require('http')
http.createServer(function (request, response) {
console.log('request come', request.url)
// response.writeHead(200, {
// 'Access-Control-Allow-Origin': 'http://127.0.0.1:8888',
// 'Access-Control-Allow-Headers': 'X-Test-Cors',
// 'Access-Control-Allow-Methods': 'POST, PUT, DELETE',
// 'Access-Control-Max-Age': '1000'
// })
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
启动之后,访问8888端口,出现如下错误
imageAccess to XMLHttpRequest at 'http://127.0.0.1:8887/' from origin 'http://localhost:8888' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
是因为跨域的问题,无法访问,那么需要设置访问权限
[图片上传失败...(image-d8a46e-1551450048215)]
同时我们可以根据url中不同的host判断,允许一些网址可以跨域访问。
8、CORS跨域限制以及预请求验证
- CORS预请求:
GET
POST
HEAD
- 允许的Content-Type:
text/plan
、multipart/form-data
、application/x-www-form-urlencoded
- 请求头限制,自定义的请求头参数必须经过认证。
- XMLHttpRequestUpload对象均没有注册任何事件监听器。
- 请求中没有使用ReadableStream对象。
9、缓存头Cache-Control的含义和使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
<script src="/script.js"></script>
</html>
http.createServer(function (request, response) {
console.log('request come', request.url)
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html)
}
if (request.url === '/script.js') {
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=20'
})
response.end('console.log("script loaded")')
}
}).listen(8888)
console.log('server listening on 8888')
image
所以一半的资源改变最好添加版本号,否则可以打包后可以没有刷新缓存。
10、缓存验证Last-Modified和Etag的使用
image image- Etag: 数据签名,数据只要修改就不一样。配合if-match 或者 if-none-match.
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html)
}
if (request.url === '/script.js') {
const etag = request.headers['if-none-match']
if (etag === '777') {
response.writeHead(304, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=2000000, no-cache',
'Last-Modified': '123',
'Etag': '777'
})
response.end()
} else {
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=2000000, no-cache',
'Last-Modified': '123',
'Etag': '777'
})
response.end('console.log("script loaded twice")')
}
}
}).listen(8888)
console.log('server listening on 8888')
11、cookie和session
- 同一域名之下的子域名可以共享cookie,cookie客户端。
- session在服务端。
12、重定向
const http = require('http')
http.createServer(function (request, response) {
console.log('request come', request.url)
if (request.url === '/') {
response.writeHead(302, { // or 301
'Location': '/new'
})
response.end()
}
if (request.url === '/new') {
response.writeHead(200, {
'Content-Type': 'text/html',
})
response.end('<div>this is content</div>')
}
}).listen(8888)
console.log('server listening on 8888')
-
302才是临时跳转。
-
301是永久跳转到新地址。如果用户没有清理缓存的话,会一直返回原来的数据,设置的时候要慎重。
13、CSP Content-Security-Policy
- 限制资源获取
- 报告资源获取越权
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html',
// 'Content-Security-Policy': 'script-src \'self\'; form-action \'self\'; report-uri /report'
})
response.end(html)
} else {
response.writeHead(200, {
'Content-Type': 'application/javascript'
})
response.end('console.log("loaded script")')
}
}).listen(8888)
console.log('server listening on 8888')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; form-action 'self';">
<title>Document</title>
</head>
<body>
<div>This is content</div>
<script>
console.log('inline js')
</script>
<script src="test.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/core.js"></script>
</body>
</html>
限制资源的加载,限制提交的范围
具体请参考: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP
image14、传输层协议
image1、UDP,用户数据报协议:
特点:
1)无连接 不用在数据传输之前进行连接和释放连接。
2)尽最大努力交付
3)面向报文 既不合并,也不拆分。
UDP面向报文的含义:
报文可大可小,既不合并,也不拆分。
应用层报文会原封不动作为传输层UDP数据报的数据部分和UDP首部组成运输层的UDP数据报。
-
UDP的功能:
复用、分用、差错检测-
复用的定义:
多个端口可以共用一个传输层UDP的数据报,再经由IP层传输出去。
分用的定义:
IP数据报拆分成UDP数据报,每个报文格式中有原端口和目的端口的标识,可以根据目的端口进行分发。
-
-
UDP差错检测:
在8字节UDP首部的末尾增加一个检验和位。
2、TCP的面向连接:
-
全双工:
一条通道双方都可以接收和发送。 -
TCP可靠传输表现在哪些方面:
- 1)无差错
- 2)不丢失
- 3)不重复
- 4)按序到达
- TCP可靠传输是通过停止等待协议实现的:
四方面理解:- 1)无差错情况
- 2)超时重传
- 3)确认丢失
- 4)确认迟到
确认迟到:
- 1)确认M1迟到,当未来某一时刻client收到确认M1,但什么也不做。
其余步骤和确认丢失相同。
确认丢失:
- 1)丢弃重传的M1报文(图中画❌的部分),
- 2)重传确认M1,因为之前丢失了。
- 客户端收到确认M1,就可以继续发送M2了。
超时重传情况:
- 1)可以保证差错检验
- 2)保证不丢失
无差错情况:
3、TCP面向字节流的概念:
- 发送方有一个缓冲区;
- 接收方也有一个缓冲区;
- 无论发送方一次给TCP缓冲区多少字节,对于TCP本身来说,会根据实际情况进行划分一次性传递多少字节,并不是发送方一次性把所有字节流一次性发给接收方。
15、HTTPS建立流程
imageHTTPS建立流程描述1:
-
Client端:发送TLS版本号,random number C,所有支持的加密算法给服务器进行协商;
-
Server端:商定好加密算法,random number S,server证书发给客户端。
-
Client端:
- 1)验证server证书;
- 2)利用预主密钥(对称加密中用到的密钥),random numberC,andom numberS三个值通过一定的算法合成会话密钥后,通过server的公钥进行加密传输。
HTTPS建立流程描述2:
- Server端:
- 1)通过私钥解密收到的会话密钥,得到预主密钥。
- 2)利用预主密钥(对称加密中用到的密钥),random numberC,andom numberS三个值通过一定的算法合成会话密钥。
HTTPS建立流程描述3:
- Client端 发送一条经过会话密钥加密的握手消息给server端;
- Server端 发送一条经过会话密钥加密的握手消息给client端;
来验证安全通道是否已经建立完成。
从 CA 证书讲起了。CA 证书其实就是数字证书,是由 CA 机构颁发的。至于 CA 机构的权威性,那么是毋庸置疑的,所有人都是信任它的。CA 证书内一般会包含以下内容:
- 证书的颁发机构、版本
- 证书的使用者
- 证书的公钥
- 证书的有效时间
- 证书的数字签名 Hash 值和签名 Hash 算法
- …
PKI体系
-
1、RSA身份验证的隐患
- 身份验证和密钥协商是TLS的基础功能,要求的前提是合法的服务器掌握着对应的私钥。但RSA算法无法确保服务器身份的合法性,因为公钥并不包含服务器的信息,存在安全隐患:
- 客户端C和服务器S进行通信,中间节点M截获了二者的通信;
- 节点M自己计算产生一对公钥pub_M和私钥pri_M;
- C向S请求公钥时,M把自己的公钥pub_M发给了C;
- C使用公钥 pub_M加密的数据能够被M解密,因为M掌握对应的私钥pri_M,而 C无法根据公钥信息判断服务器的身份,从而 C和 M之间建立了"可信"加密连接;
- 中间节点 M和服务器S之间再建立合法的连接,因此 C和 S之间通信被M完全掌握,M可以进行信息的窃听、篡改等操作。
另外,服务器也可以对自己的发出的信息进行否认,不承认相关信息是自己发出。
因此该方案下至少存在两类问题:中间人攻击和信息抵赖。
-
2、身份验证CA和证书
- 解决上述身份验证问题的关键是确保获取的公钥途径是合法的,能够验证服务器的身份信息,为此需要引入权威的第三方机构CA(如沃通CA)。CA 负责核实公钥的拥有者的信息,并颁发认证"证书",同时能够为使用者提供证书验证服务,即PKI体系(PKI基础知识)。
- 基本的原理为,CA负责审核信息,然后对关键信息利用私钥进行"签名",公开对应的公钥,客户端可以利用公钥验证签名。CA也可以吊销已经签发的证书,基本的方式包括两类 CRL 文件和 OCSP。CA使用具体的流程如下:
- a.服务方S向第三方机构CA提交公钥、组织信息、个人信息(域名)等信息并申请认证;
- b.CA通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法,是否拥有域名的所有权等;
- c.如信息审核通过,CA会向申请者签发认证文件-证书。
证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构 CA的信息、有效时间、证书序列号等信息的明文,同时包含一个签名;
签名的产生算法:首先,使用散列函数计算公开的明文信息的信息摘要,然后,采用 CA的私钥对信息摘要进行加密,密文即签名; - d.客户端 C 向服务器 S 发出请求时,S 返回证书文件;
- e.客户端 C读取证书中的相关的明文信息,采用相同的散列函数计算得到信息摘要,然后,利用对应 CA的公钥解密签名数据,对比证书的信息摘要,如果一致,则可以确认证书的合法性,即公钥合法;
- f.客户端然后验证证书相关的域名信息、有效时间等信息;
- g.客户端会内置信任CA的证书信息(包含公钥),如果CA不被信任,则找不到对应 CA的证书,证书也会被判定非法。
在这个过程注意几点:
- a.申请证书不需要提供私钥,确保私钥永远只能服务器掌握;
- b.证书的合法性仍然依赖于非对称加密算法,证书主要是增加了服务器信息以及签名;
- c.内置 CA 对应的证书称为根证书,颁发者和使用者相同,自己为自己签名,即自签名证书(为什么说"部署自签SSL证书非常不安全")
- d.证书=公钥+申请者与颁发者信息+签名;
正好我们把客户端如何校验 CA 证书的步骤说下吧。
CA 证书中的 Hash 值,其实是用证书的私钥进行加密后的值(证书的私钥不在 CA 证书中)。然后客户端得到证书后,利用证书中的公钥去解密该 Hash 值,得到 Hash-a ;然后再利用证书内的签名 Hash 算法去生成一个 Hash-b 。最后比较 Hash-a 和 Hash-b 这两个的值。如果相等,那么证明了该证书是对的,服务端是可以被信任的;如果不相等,那么就说明该证书是错误的,可能被篡改了,浏览器会给出相关提示,无法建立起 HTTPS 连接。除此之外,还会校验 CA 证书的有效时间和域名匹配等。
接下来我们就来详细讲一下 HTTPS 中的 SSL 握手建立过程,假设现在有客户端 A 和服务器 B :
- 首先,客户端 A 访问服务器 B ,比如我们用浏览器打开一个网页 https://www.baidu.com ,这时,浏览器就是客户端 A ,百度的服务器就是服务器 B 了。这时候客户端 A 会生成一个随机数1,把随机数1 、自己支持的 SSL 版本号以及加密算法等这些信息告诉服务器 B 。
- 服务器 B 知道这些信息后,然后确认一下双方的加密算法,然后服务端也生成一个随机数 B ,并将随机数 B 和 CA 颁发给自己的证书一同返回给客户端 A 。
- 客户端 A 得到 CA 证书后,会去校验该 CA 证书的有效性,校验方法在上面已经说过了。校验通过后,客户端生成一个随机数3 ,然后用证书中的公钥加密随机数3 并传输给服务端 B 。
- 服务端 B 得到加密后的随机数3,然后利用私钥进行解密,得到真正的随机数3。
- 最后,客户端 A 和服务端 B 都有随机数1、随机数2、随机数3,然后双方利用这三个随机数生成一个对话密钥。之后传输内容就是利用对话密钥来进行加解密了。这时就是利用了对称加密,一般用的都是 AES 算法。
- 客户端 A 通知服务端 B ,指明后面的通讯用对话密钥来完成,同时通知服务器 B 客户端 A 的握手过程结束。
- 服务端 B 通知客户端 A,指明后面的通讯用对话密钥来完成,同时通知客户端 A 服务器 B 的握手过程结束。
- SSL 的握手部分结束,SSL 安全通道的数据通讯开始,客户端 A 和服务器 B 开始使用相同的对话密钥进行数据通讯。
到此,SSL 握手过程就讲完了。可能上面的流程太过于复杂,我们简单地来讲:
- 客户端和服务端建立 SSL 握手,客户端通过 CA 证书来确认服务端的身份;
- 互相传递三个随机数,之后通过这随机数来生成一个密钥;
- 互相确认密钥,然后握手结束;
- 数据通讯开始,都使用同一个对话密钥来加解密;
我们可以发现,在 HTTPS 加密原理的过程中把对称加密和非对称加密都利用了起来。即利用了非对称加密安全性高的特点,又利用了对称加密速度快,效率高的好处。真的是设计得非常精妙,令人赞不绝口。