web5-HTTP/HTTPS

一:http 

1、http报文认识

Remote Address :访问目标 URL 解析出来的 IP 地址, 443 :表示当前 https 协议。
Referrer Policy : Referrer 用户指明当前请求的来源页面,对于同源的请求,会发送完整的url 作为引用地址,防盗链。

accept :请求可以支持的响应格式列表信息
accept-encoding :告知服务器本地浏览器支持是压缩方式
sec-fetch-dest :期望获得什么类型的资源
sec-fetch-mode navigate ,表示这是一个浏览器的页面切换请求
sec-fetch-site :表示一个请求发起的来源和目标资源来源之间的关系, cross-site:跨域请求, same-origin:同源请求。 none: 不是同源也不是跨域,请求与任意上下文无关。比如浏览器窗口中拖放一个文件
sec-fetch-user: 表示一个导航请求是否由用户激活触发(如用户主动点击鼠标、键盘)
Sec-Fetch-User: ?0   否                Sec-Fetch-User: ?1   是
upgrade-insecure-requests :1,       浏览器告诉服务器,浏览器是可以处理https 请求的,即使访问的 https 请求中又包含了其他的 http 请求。
user-agent :描述浏览器的信息

 

response中的属性,基本都是和request中对应上 ,在request支持的里面选择一个使用。
介绍两个:
server :部署web应用 的容器。  openresty :封装了 Nginx 以及第三方的类库,Lua 语言, redis 等。
vary :accept-Encoding。指的是参考request中的accept-Encoding属性,支持的是什么格式,然后,告诉代理服务器缓存对应格式的资源(压缩、不压缩)。   代理服务器指的是,各地区的边缘服务器,如cdn,不可能所有请求都去请求主服务器。

2、http链接管理-TCP

http通信是基于tpc/ip承载的,https多了一个安全层,所以https是安全的。

 TCP就是为了建立链接,传输数据的一种管道。 一个设备可以同时有多个管道,所以TCP使用一个信封一样的东西:TCB数据结构,把每个管道的数据包起来隔离开。

TCP建立连接需要三次握手,释放连接需要四次挥手:

1、三次握手:

0 )准备工作
最开始的时候客户端和服务器都是处于 CLOSED 状态。主动打开连接的为客户端,被动打开连接的是服务器。
TCP 服务器进程先创建传输控制块 TCB ,时刻准备接受客户进程的连接请求, 此时服务器就进入了LISTEN (监听)状态。

1 )一次握手:
TCP 客户端创建传输控制块 TCB ,然后向服务器发出连接请求报文, 报文首部中的同步位SYN=1表示我要发起同步,建立连接 ,同时选择一个初始序列号 seq=x ,就是一个标识,后续需要累加
此时,TCP 客户端进程进入了 SYN-SENT (同步已发送状态)状态。
TCP 规定,SYN 报文段( SYN=1 的报文段)不能携带数据,但需要消耗掉一个序号。
2 )二次握手:
TCP服务器收到请求报文后,如果同意连接,则发出确认报文:
ACK=1,表示同意并确认要建立连接(0则表示不包含确认信息)。
ack=x+1,确认号,确认的是上一条客户端发起的那个请求。
SYN=1,表示服务端也要发起同步
seq=y ,向客户端发起同步,也需要一个自己的序列号。
此时TCP 服务器进程进入了 SYN-RCVD(同步收到)状态。
3 )三次握手:
TCP 客户端收到确认后,还要向服务器给出确认。确认报文的 ACK=1
ack=y+1确认的是服务端发过来的序列号 ,自己的序列号 seq=x+1 ,此时, TCP连接建立,客户端进入ESTABLISHED (已建立连接)状态。
TCP 规定, ACK 报文段可以携带数据,但是如果不携带数据则不消耗序号。
当服务器收到客户端的确认后也进入 established 状态,此后双方就可以开始 通信了。

为什么需要第三次握手?
表面上看,两次握手后即可建立连接,但是并不严谨,比如:客户端发了同步请求,由于网络或其他原因,服务端暂时没有收到,于是客户端重试又发送了一条一模一样的请求。  经过两次握手后建立了连接并传输了数据,第一次的请求恢复正常到达了服务端,于是,服务端给出了回应。 这样又会再次建立连接传输数据,造成资源开销和浪费。 
有了第三次握手后,,客户端就算收到了重试那一条的确认,但是它知道已经建立过相同的链接,就不会再进行第三次握手了。
也从另一个角度看:
第一次握手: 客户端只确认了自己的发送能力OK,,服务端只确认了自己的接受能力OK。
第二次握手:客户端确认了自己和服务端的发送能力OK、接受能力OK;  但是服务端只知道自己的接受和发送能力OK,客户端发送能力OK(服务端并不知道客户端是否接受到自己的回应)。
第三次握手:服务端和客户端,都确认了自己和对方的发送和接受能力。
 
TCP 协议缺陷
 
DDOS又称为分布式拒绝服务,就是不应答正常的请求。 比如经典的 SYN flood攻击就会造成这样的情况:
1. SYN flood 在攻击时,首先伪造大量的源 IP 地址,分别向服务器端发送大量的SYN 包。
2. 服务器端返回 SYN/ACK 包,因为源地址是伪造的,所以伪造的 IP 并不会应答。
3. 服务器端没有收到伪造 IP 的回应,会重试 3~5 次并且等待一个 SYN Time —般为30 秒至 2 分钟),如果超时则丢弃这个连接。
4. 攻击者大量发送这种伪造源地址的 SYN 请求,服务器端将会消耗非常多的资源来处理这种半连接,同时还要不断地对这些IP 进行 SYN+ACK 重试。
5. 最后的结果是服务器无暇理睬正常的连接请求,导致拒绝服务。

2、四次挥手:
 

 数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于established(表示连接已经建立)状态,然后客户端主动关闭,服务器被动关闭。

1 TCP 客户端发送一个 FIN ,用来关闭客户到服务器的数据传送(和SYN相反)。
2 ) 服务器收到这个 FIN ,它发回一个 ACK ,确认序号为收到的序号加 1。当然,此次确认也需要带上自己的一个seq,标识一次独立的请求应答。
3 ) 服务器关闭客户端的连接,主动在发送一个 FIN 给客户端,标识我也要关闭( 为什么这个FIN不放到第2步呢)
4 ) 客户端发回 ACK 报文确认,并将确认序号设置为收到序号加 1
为什么服务端的FIN不放到第2步呢?
关闭连接时,服务器收到对方的 FIN 报文时,仅仅表示对方不再发送数据了,但对方还能接收数据,而自己也未必已经将全部数据都发送给对方 了,所以自己可以立即关闭,也可以发送一些数据给对方后,再发送FIN 报文给对方来表示同意现在关闭连接,因此,服务器的ACK FIN 一般都会分开发送,从而导致多了一次,这也是握手三次,挥手需四次的原因。

如果已经建立了连接,但是客户端突然出现故障了怎么办?

服务端有一个计时器,每次收到客户端请求都会复位,若2小时内没有收到数据,就会发送探测报文,后每隔75s探测一次,共10次,没有反馈就关闭连接。

3、TCP的数据传输方式-滑动窗口协议

TCP传输是将报文切割成多个,按顺序分段传输的,因为网络有流量大小限制,每段大小一般默认为536字节,接收后按顺序拼接起来。  那么问题来了,如果串行发送,效率太低;如果并行发送,可能造成网络阻塞。  怎样来把握这个度呢?

使用滑动窗口,根据段数据大小和网络情况,每次发送合适数量的段,以及该发送哪一些。如图:

灰色:已发送且已确认。

黄色:已发送还没确认。

绿色:等待发送,还没确认。

白色:未发送,也没确认。

滑动窗口管理的就是中间两部分数据。 比如,当5被确认后,窗口就会右移一格,5变成灰色,将10发出去变成黄色,12加入内存准备发送,变成绿色。(当然,并不一定只处理一段)

超时重发: 

滑动窗口可以保证每一段是否被成功发送并确认,但是,万一5阻塞了,一直等待5的ACK,就没法把12号加进来。。 这时候就会对5做重试,原理是有一个超时重传时间RTO,决定重试的等待时间:默认首次是6s,后续已2,4的倍数指数增长。  当重试次数达到限制后,认为有问题,关闭连接。

4、TCP性能解析

http事务消耗的时间,主要有:

  • DNS解析,将url中域名解析为服务器的ip。
  • 建立TCP连接(三次握手)。
  • 网络传输报文和服务端处理报文都需要时间。
  • 服务端回传报文。

这些问题和网络,设备性能都有很大关系。 假如网络和设备都很好的情况下,性能就主要看TCP了。

创建TCP的性能问题:

  • TCP建立需要三次握手:
    • 建立一个http链接,需要建立一个TCP,经过三次握手耗时。  如果打开一个页面需要访问很多个http,耗时一下就上去了。
  • TCP慢启动:
    • 上面说了滑动窗口可以根据网络确定发送多少个分组的报文,如何确定的? 其实就是TCP建立后,不断的探测网络情况,第一次发送一个,第二次发送两个,四个… 直到达到合适的量。     如果一个http中包含了很多个分组报文,是不能一次发送完的。
  • 延迟确认ACK:
    • 在进行ACK确认时,为了有效利用网络,会尽量”捎带“一些其他的输出数据。 也就是说并不会马上给对方返回ACK确认,而是要等待一定的时间,这段时间内看看有没有数据可以顺带返回。  如果没有,就白等了,也就是延迟确认ACK这种算法,一定程度上会造成性能降低。

5、http特性

基于TCP的特点,可以想象,如果打开一个页面时访问了多个http,也就是要建立多个TCP请求,假如串行来执行,那页面加载得多慢啊。   http如何使用TCP才能达到性能最优呢?

  • 并行连接
    • 多个TCP并行执行,不用等第一个完成关闭后,再创建第二个。  (当然了,不可能说有多少个http请求,就并行创建多少个TCP,那不得把服务器耗死! 比如浏览器实际上是对相同域名的http请求-同源请求,做了并行创建TCP数量的限制。 大部分浏览器是6个。) 
  • 持久连接
    • Connection: Keep-Alive
    • 对于访问频率高的站点的链接,可以始终保持打开复用,避免每次创建TCP的消耗。 HTTP/1.1 (以及 HTTP/1.0 的各种增强版本)开始都支持了。 当然,同一个连接中,多个http事务是串行的(一个结束后才能开始下一个)
  • 管道化连接
    • 基于持久连接来说,为了提升性能,一个TCP中让多个http事务不串行,在响应到达之前, 可以将多条请求放入队列。 当第一条请求通过网络流向另一端的服务器时, 第二条和第三条请求也可以开始发送了。
    • 对管道化连接有几条限制:
      • 必须按照请求相同的顺序返回报文。 因为http请求报文中没有序列号标签,一旦失序,就会造成错乱。  因此,必须按序响应。但是,某个请求如果耗时太长,容易导致后面的请求堆积阻塞。
      • 客户端必须准备好连接可能随时关闭。 出现这种情况时,客户端须能正确的应对并重新发送失败的请求。
      • 只有幂等请求能管道化。  幂等请求如GET,浏览器可以缓存get的url,每次都返回一样的结果,但是POST为写操作,每次可能创建新的资源。
对于上面介绍的http特性,都是在http的发展进程中不断优化的。 像什么持久连接,管道技术,都是HTTP1.1才有的。 在进入http2后,性能才有了飞跃。下面看看http2的特性。

6、http2新特性

  • 二进制分帧:http2性能增强的核心

HTTP 2.0 会将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码 ,其中HTTP1.x的首部信息会被封装到Headers 帧,而我们的request body则封装到Data帧。

帧是数据传输的最小单位, 以二进制传输代替原本的明文传输。

  • Connection-连接:HTTP 2.0 所有的通信都在一个TCP连接上完成,这个连接可以承载任意数量的双向数据流。
  • Stream-数据流:数据流以消息的形式发送,每个数据流包含多条消息。 每个流有唯一整数标识符,为了防止两端流ID冲突,客户端发起的流具有奇数ID,服务器端发起的流具有偶数ID。
  • Message-消息:消息由一或多个帧组成。  比如一系列DATA帧和一个HEADERS帧组成了一个完整的http请求消息。
  • Frame-帧:每个帧首部都有一个流标识,代表属于哪个流,所以可以乱序发送,接收端重新组装。

对于http的性能来说,关键就在于低延迟而非高宽带。  通常http请求的时间都很短,甚至是突发性的。 如果在这短时间内,还要花非大部分时间去创建和关闭TCP连接,性能肯定差。 反之,如果能让一个TCP连接长时间保持,所有http都共用它,那么创建TCP的性能问题就可以忽略不计。 http2.0就是很好的利用了这一点。

  •  头部压缩

        在 HTTP/1 中,HTTP 请求和响应都是由「状态行、请求 / 响应头部、消息主

体」三部分组成。 一般body部分都会用gzip压缩后传输(或本身就是压缩过的二进制文件,如图片音频),但header部分都是纯文本传输的,现在很多页面中都会产生几十上百个请求,header部分也会占用很大部分的流量。
 
        HTTP/2协议中定义了新的压缩方法 HPACK,它需要浏览器和服务端都支持HTTP2。 原理是两端维护一份相同的静态表一份相同的动态表。 有了相同的表后,header传输时就可以传递索引值了,接收方根据索引值解析出内容。
  •         静态表: header中预定义好 不可更改的通用名称和value,有61个

        动态表:在静态表中不存在的,比如自定义的值。 当然,第一次肯定需要明文发送,接收端才能维护,后续就可以传输索引了。
  • 多路复用
    •  http1.1后虽然可以管道化连接,但是浏览器对同一个域名下的请求有数量限制,超过限制数目的请求会被阻塞。这也是为何一些站点会有多个静态资源CDN 域名的原因之一。
    • http 2.0 连接都是持久化的,而且客户端与服务器之间也只需要一个连接(每个

      域名一个连接),http2连接可以承载数十或数百个流的复用,多路复用意

      味着来自很多流的数据包能够混合在一起传输。当到达终点时, 再根据不同帧首部的流标识符重新连接将不同的数据流进行组装。
      虽然多个流可以混在一起,但是同一个Stream中的Frame必须按顺序,因为Frame中的标识是流的id,只能区分出他们是属于哪个流。 
  • 服务器推送
  • 服务器可以对一个客户端请求发送多个响应,向客户端推送资源无需客户端明确地请求。比如服务端能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端多次请求的步骤(当一个请求是由客户端主页发起的,服务器很可能会响应主页内容、logo 以及样式表等多种资源),大大提升速度。
  • 另外,服务器推送还有一个很大的优势:可以缓存!也让在遵循同源的情况下,不同页面之间可以共享缓存资源成为可能。
  • 当服务端需要主动推送某个资源时,便会发送一个 Frame Type 为 PUSH_PROMISE 的 Frame ,里面带了 PUSH 需要新建的 Stream ID 。意思是告诉客户端:接下来我要用这个 ID 向你发送东西,客户端准备好接着。客户端解析 Frame 时,发现它是一个 PUSH_PROMISE 类型,便会准备接收服务端要推送的流。

经过http2后,对TCP的利用已经达到了极限,所有的性能瓶颈几乎都集中在了TCP本身的特性上,所以http3中,已经开始用UDP替代TCP了。在未来http3肯定会成为主流。

二:https

https就是在http基础上加了一层安全层SSL/TLS(就是一种安全协议,SSL现在几乎没有使用了,都是用的TLS),作用只是让http传输更安全。 所以https和http1 或者 http2、http3没有关系,不管是哪个http的版本,只要配置了安全层,就是https(只不过http2及后续版本的http,默认都必须要配置安全层,也就是要以https为基础)。

 那https是如何安全的? 其实就是在客户端和服务器之间提供一座桥梁,帮助双方约定出一种加密算法传输密文,只有双方自己可以解析。 认识https前,先看看几个基本概念:

对称加密:

加密和解密都使用同一个密钥,常用算法:

  • DESData Encryption Standard):数据加密标准(加密强度不够,能够暴力破解)
  • 3DES:原理和DES几乎是一样的,只是使用3个密钥,对相同的数据执行三次加密,增强加密强度。(缺点:要维护3个密钥,大大增加了维护成本)
  • AESAdvanced Encryption Standard):高级加密标准,用来替代原先的DES,目前美国国家安全局使用的,苹果的钥匙串访问采用的就AES加密。是现在公认的最安全的加密方式,是对称密钥加密中最流行的算法。 AES128和AES256主要区别是密钥长度不同(分别是128bits,256bits)、加密处理轮数不同(分别是10轮,14轮),后者强度高于前者。

优点:速度快、加密效率高。   

缺点:只有一个秘钥,不安全, 假如被拦截容易被破解。

非对称加密:

有一对秘钥,公钥和私钥。   公钥加密的需要私钥才能解,反过来一样。 常用算法:

RSA DSA ECDSA等。
优点:算法复杂难以破解,公钥存在客户端,私钥存在服务端,不用像对称加密那样传输秘钥,安全性提高很多。
缺点:效率没有对称加密的高,另外,公钥是公开的,也可能会泄露。

既然上面两种加密方式各有优缺点,那可以把两种方式结合。  传输的业务数据用对称加密方式(效率高),但是呢,对称加密的秘钥,我们先用非对称加密的方式把它加密,保证此秘钥的安全。 如下图:

此时还有问题,假如客户端请求一开始就被黑客拦截了, 也就是说一直请求的是个假Server,它自己不知道,那还是有问题!如下图:

 要解决这个问题,那就还要验证两个东西:

1、传输的报文有没有被篡改过。  解决:数字签名。

2、Server端的身份是否被伪装。  解决:数字证书。

数字签名:确定消息是由发送方发出的,以及消息的完整性

 发送方:将消息通过hash算法生成消息摘要, 接着用私钥加密摘要得到数字签名,并把消息原文和数字签名一起发送。

接收方:通过公钥解密数字签名,得到摘要1(能解密说明消息确实是发送方发出来的,私钥和公钥能对上),再通过相同的hash算法对消息原文生成摘要-摘要2并与摘要1对比(只要摘要1和2相同,说明消息未被篡改)。

数字证书:证明消息发送方是合法的

数字证书就像一个身份证,是权威机构-认证中心 颁发给发送方(服务端)的,证明服务端是合法的。而权威证书颁发机构(Certificate Authority,简称CA)在全球的数量并不多,因此,一个合法的服务端必定要先从CA申请证书。

实际上,我们使用的证书分很多种类型,SSL证书只是其中的一种,用于加密HTTP协议,也就是HTTPSTLS。如果一个web应用想要升级为https,需要购买证书。

流程如下:

  1. 服务器的运营人员向第三方机构CA(或者其代理机构)提交公钥、组织信息、域名等信息并申请认证;
  2. CA通过线上、线下等多种手段验证申请者提供信息的真实性。
  3. 如信息审核通过,CA会向申请者签发认证文件-证书。证书内容包含申请者公钥、申请者的组织信息和个人信息、签发机构 的信息、有效时间、证书序列号等信息的明文,同时包含一个CA机构的数字签名。 其中签名的算法是使用散列函数计算上述的明文信息,得到的信息摘要,再采用 CA的私钥对信息摘要进行加密,也就是证书本身的签名;
  4. 客户端(如浏览器)和服务端进行https通信时,首先就要请求获取服务端的CA证书并验证。 而浏览器或操作系统等,都是提前内置了很多CA的证书,也就是具有CA的公钥信息(所以操作系统什么的,要用正版)。 这个时候,当请求到服务端的证书签名时,就能解密并验证证书内容是否合法。
  5. 如果证书合法,就能拿到服务器的CA证书的内容,也就是可以拿到服务端的公钥了(这样,公钥一定是安全的,服务器也一定是合法的)。 接下来,双方就可以按照上面介绍的,通过非对称加密和对称加密传输业务数据。
SSL 证书分类:
DV (域名型 SSL ):个人站点
OV (企业型 SSL ):企业官网
EV (增强型 SSL ):对安全需求更强的企业官网、电商、互联网金融网站

三:http升级到https

介绍完https后,如何把http升级为https? 首先先申请CA证书,然后在服务端配置使用https即可。 流程如下:

  • 申请证书:
  • 下载证书:
    • 购买成功后,登录阿里云控制台下载即可。 解压后得到两个文件:证书文件和秘钥文件
  • Nginx配置https:
    • Nginx 安装目录(默认为 /usr/local/nginx/conf ) ,创建cert 目录,并将解压的两个文件上传到此目录。
    • 配置文件配置:vim /usr/local/nginx/conf/nginx.conf, 配置如:
      • # 以下属性中以 ssl 开头的属性代表与证书配置有关,其他属性请根据自己的需要
        进行配置。
        server {
        # 配置 HTTPS 的默认访问端口号为 443 。此处如果未配置 HTTPS 的默认
        访问端口,可能会造成 Nginx 无法启动。
        #Nginx 1.15.0 以上版本请使用 listen 443 ssl 代替 listen 443
        ssl on
        listen 443 ssl;
        # www.certificatestests.com 修改为您证书绑定的域名,例
        如: www.example.com 。如果您购买的是通配符域名证书,
        # 要修改为通配符域名,例如: *.aliyun.com
        server_name www.certificatestests.com;
      • # 将所有 HTTP 请求通过 return 重定向到 HTTPS(用户输域名时不加https,默认走http,需要自动跳转https)
        return 301 https:// $server_name$request_uri ;
        root html;
        index index.html index.htm;
        # domain name.pem 替换成您证书的文件名称。
        ssl_certificate cert/domain name.pem;
        # domain name.key 替换成您证书的密钥文件名称。
        ssl_certificate_key cert/domain name.key;
        ssl_session_timeout 5m;
        # 使用此加密套件。
        ssl_ciphers ECDHE-RSA-AES128-GCM-
        SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        # 使用该协议进行配置。
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        location / {
        # 站点目录。
        root html;
        index index.html index.htm;
        }}
  • 保存并加载配置:
    • cd /usr/local/nginx/sbin         
    • ./nginx -s reload 

配置完成后,访问一下域名,就可以看到域名地址栏加上了一把锁。 

四:https性能优化

https在http的基础上,增加了安全层,性能比http要差,主要体现在:

1. 协议交互所增加的网络 RTT(round trip time:一次客户端和服务器交互的往返时间)

因为除了要经过TCP三次握手,还需要经过TLS安全协议握手确定安全性、加密方式等。另外,很多用户习惯于使用HTTP请求你的网站,首先要让用户强制302/301HTTPS,这次跳转至少增加1RTT的延时;301跳转后要再次TCP建连,又要增加1RTT的延时。

这里顺便解释一下301和302:

如果web应用支持https,客户端访问http://xxx,服务器收到请求之后(Nginx)发现请求的是http请求,可以返回301告知浏览器重新发出请求。 重定向状态码与原因短语:

301 redirect: 301 代表永久性转移(Permanently Moved)
302 redirect: 302 代表暂时性转移(Temporarily Moved )
2. 加解密相关的计算耗时。 在传输重对数据的加解密,每一步都会经过复杂的计算,对称、非对称,数据签名,证书验证等等…

 优化策略:

1、False start

常规情况下,T​​​​​SL握手协议会经过两个RTT,双方发送随机数和服务端发送证书,已经客户端发送加密列表等服务端确认加密方式。用抓包工具可以看到发送业务数据Application Data前经过了两个RTT

 false start 为抢跑的意思,不等待第二个RTT确认加密算法,直接发送业务数据。 针对的是加密方式确认的过程,因为目前主流通用的加密方式,客户端默认都支持,所以直接由服务端自己配置并决定就好了,省去由客户端先发送加密方式列表服务端确认约定的过程。

如何开启呢? 

首先,现在大部分客户端(浏览器)默认都是支持此模式的,服务端nginx中只需要配置常用加密方式并开启此模式即可。

# server {

# listen 443 ssl http2 default_server;
# listen [::]:443 ssl http2 default_server;
# server_name _;
# root /usr/share/nginx/html;
#
# ssl_certificate "/etc/pki/nginx/server.crt";
# ssl_certificate_key
"/etc/pki/nginx/private/server.key";
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 10m;
ssl_ciphers ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
#
# #......
# }

2、升级到http2

上面已经学过http2的优势,在性能方面有很大提升。 所以将https升级到http2也会有很大改善。

配置很简单,就是在443端口后面指明http2

Version:0.9 StartHTML:0000000105 EndHTML:0000003722 StartFragment:0000000141 EndFragment:0000003682

# server {
# 升级到 https 后的 web 应用是基于 http2 协议进行传输
listen 443 ssl http2;
# listen [::]:443 ssl http2 default_server;
# server_name _;
# root /usr/share/nginx/html;
#
# ssl_certificate "/etc/pki/nginx/server.crt";
# ssl_certificate_key
"/etc/pki/nginx/private/server.key";
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 10m;
# ssl_ciphers ECDHE-RSA-AES256-SHA384:AES256-
SHA256:RC4:HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
#
# #......
# }

3、会话恢复

SSL/TLS 中的 session HTTP session 类似,都是用来保存客户端和服务端之间交互的一些记录,这里的SSL session 保存的是 SSL 的握手记录。  如果客户端和服务端之间能重用之前已经握手成功的session,就不用重新握手。  有两种技术可以达到session重用:
1)session id
一次 TLS 握手的结果是建立一条对称加密的数据通道,这条数据通道相关的参
数都可以在内存中保存的,也就是缓存Session Cache,所以服务端就可以针对这一套参数值生成一个 ID , 叫做Session ID ,使用该 ID 就可以直接复原对称加密的通信通道。所以当客户端下一次请求(Client Hello )到达的时候,客户端如果携带了 Session ID ,服务端就可以根据这个Session ID 找到对应的 Secure context ,从而复原信道了。
但是, Session Cache 是不能够跨进程共享的,而 Nginx 是一个多进程的 业务模型,如果每一个进程一份独立的 Session Cache ,就会导致同样一个 SSL 连接,上一次被一个 Worker 进程服务,下一次是被另外一个 Worker 进程服务 的。使得整个 OpenSSL 在内存上造成极大的浪费,并且使得 Cache 的准确性大 幅度下滑

所以Nginx就另外又实现了一套自己的Session Cache系统,这套系统是跨进程的,使用的是共享内存,用红黑树的方式组织的Session Cache,并且对Session ID的定义也重新进行了封装,最终形成了一个跨进程了Session Cache。

Session ID共享复用Nginx可以通过ssl_session_cache设置,具体有很多选项,可以看下面:

server {
# ......
# ssl_session_cache off | none | [builtin[:size]]
[shared:name:size];
# off: 使用会话缓存是严格被禁止的 : nginx 明确告诉客户端会
话不可以重复使用。
# none: 使用会话缓存是温和地不允许的 : nginx 告诉客户端会话
也许可以重用,但并不会真的在缓存中保存会话参数。
# builtin: 内置于 OpenSSL 的缓存 ; 只被一个 worker 进程使用 . 缓存大小用会话作为单位被指明。 如果缓存大小未被给出,它就等于 20480 个会话。使用内置缓存会引起内存碎片。
# shared: 一个缓存被所有 worker 进程共享。缓存大小用 bytes 作为单位被指明。1MB 可以存储大约 4000 个会话。  每一个共享的缓存应该有一个唯一的名称。拥有相同名称的缓存可以被多个虚拟服务(virtual server) 所使用。
ssl_session_cache shared:SSL:10m;
# ......
}
 
Session id  机制的弊端:
1、负载均衡中,多机之间往往没有同步 session 信息,如果客户端两次请求没有落在同一台机器上就无法找到匹配的信息;
2、服务端存储 Session ID 对应的信息不好控制失效时间,太短起不到作用,太长又占用服务端大量资源。
ssl_session_timeout 5m;

2)session ticket
session ticket可以解决上述的问题,因为session ticket服务端加密过的会话信息,秘钥只有服务端知道,ticket发给客户端保存,请求时候带上,只要服务端能成功解密就能快速完成握手。   这样,服务端就不用缓存,也就不会有上述的问题。  
在分布式系统中,需要要求各服务节点(nginx节点)使用相同的密钥,这样增加了风险系数,所以,需要隔一段时间就换一下秘钥。
配置:
# 开启 Session Ticket ,此处没有指定加密算法, openssl 默认会生成随机数的key
 
# 如果需要手动指定,配置:
ssl_session_ticket_key encode_decode.key;
# key 文件可以由 openssl 命令生成,例如: openssl rand 80> ticket.key
# 如果存在 Nginx 集群,多个集群应用使用同一份 key 文件,为保证安全性须每
隔一定频率更换 key
ssl_session_tickets on;

4、HSTS-严格传输安全(强制使用https)

如果用户输入http,经过301/302跳转https,  会多一个RTT时间。  另外,在真正访问https前,也存在不安全隐患。

HSTS是一种新的web安全协议,作用是强制客户端(如浏览器)使用HTTPS与服务器创建连接,而不是HTTP。原理:

第一次通过HTTPS请求,服务器响应Strict-Transport-Security 头,以后尝试访问这个网站的请求都会自动把HTTP替换为HTTPS。(第一次访问https也有可能是http经过301/302跳转,但只会有一次http,后面就不会有了)。

HSTS 头设置的过期时间到了,后面通过 HTTP 的访问恢复到正常模式,不会
再自动跳转到 HTTPS 。 所以每次浏览器接收到Strict-Transport-Security 头,它都会更新这个网站的过期时间,所以网站可以刷新这些信息,防止过期发生。
开启方法:
 
server {
listen 443 ssl http2;
server_name xx.com;
#1. max-age :单位:秒。 HSTS header 过期时间,一般设置为 1 年,
31536000 秒。 而每次Response Header 都带上 HSTS Header ,则可不断刷新其过期
时间。
#2. includeSubDomains :需要开启 HSTS 的域名 / 子域名。
# 在接下来的一年(即 31536000 秒)中,浏览器只要向xxx或其子域名( includeSubDomains )发送HTTP 请求时,必须采用 HTTPS 来发起连接。比如,用户点击超链接或在地址栏输入 http://xxx/ ,浏览器应当自动将 http 转写成 https ,然后直接向https://xxx/ 发送请求。
add_header Strict-Transport-Security "max-age=31536000;  includeSubDomains" always;
location / {
}
}

在公共场所,例如免费WIFI的环境中,第一次访问网站会弹出HSTS错误:

 

猜你喜欢

转载自blog.csdn.net/growing_duck/article/details/126852519