游客发表

Nginx配置HTTPS详解

发帖时间:2024-05-08 13:44:08

Nginx配置HTTPS详解

前向知识:

  • SSL/TLS: 阅读本文之前,详解请先了解SSL/TLS,详解 不熟悉请参考我的另外一篇文章:SSL/TLS,TLSv1.2是详解当前使用最多的版本,而TLSv1.3是详解马上将会普及得版本。所以下面的详解配置会同时涉及到这两个版本。
  • Nginx编译安装:Nginx的详解安装可以使用各发行版的包管理器,比如yum,详解 dnf, apt。也可以编译安装。详解

测试TLS安全性: https://www.ssllabs.com/ssltest/analyze.html

TLS配置: https://ssl-config.mozilla.org/ HTTPS

Nginx配置博客:https://segmentfault.com/a/1190000013380916

配置在后端web服务器上,详解Nginx 模块: ngx_http_ssl_module

TLS/SSL协议是详解TCP的上层协议,不支持在四层设备上面配置SSL/TLS,详解 只能在后端web服务器上面配置

在七层设备上面部署HTTPS

部署在CDN上面

  • 操作系统:Fedora33,CentOS8即可,详解若是详解CentOS7及以下要使用TLSv1.3需要自己升级OpenSSL到1.1.1版本
  • OpenSSL: 1.1.1k
  • Nginx: 1.18.0,Nginx1.16.0及以上的详解就可以。

Nginx要支持SSL/TLS,详解需要安装ngx_http_ssl_module模块,使用发现版的安装工具默认已安装,自己编译安装如下:

$ https://zhuanlan.zhihu.com/p/configure --with-http_ssl_module$ make


本文使用编译安装的Nginx演示,准备环境:

# 源码目录创建一个tmp目录来存放nginx.conf,access.log$ mkdir tmp# 启动Nginx命令$ objs/nginx -p $(pwd) -c tmp/nginx.conf


Wireshark抓包环境设置 配置环境变量SSLKEYLOGTFILE和Wireshark Pre-Master-Secret log filename指向相同的日志文件

  1. 配置环境变量
$ export SSLKEYLOGFILE=/mnt/c/Users/slynxes/Desktop/sslkey.log

2. 配置Wireshark

编辑 --> 首选项 --> TLS


Nginx开启HTTPS,最少配置项,其他采用默认配置

daemon off;pid tmp/nginx.pid;error_log stderr debug;events { }http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl;        # SSL 协议版本        ssl_protocols TLSv1.2;        # 证书        ssl_certificate ssl/ecc.pem;        # 私钥        ssl_certificate_key ssl/ecc.key;        return 200 "https ok \n";    }}

版本:TLSv1.2, 时间:1-RTT False Start 指还未完成SSL/TLS握手协议,就进行HTTPS请求/应答。可以将TLSv1.2版本2-RTT的握手时间缩短为1-RTT,RSA密钥协商算法是不支持False Start的,只有ECDHE密钥协商算法才支持。Nginx只要ssl_ciphers套件里面有支持ECDHE的,浏览器自动会使用False Start。

不使用False Start的SSL/TLS握手过程




使用False Start的SSL/TLS握手过程


验证使用RSA密钥协商算法不能开启False Start

ssl_ciphers套件中不包含ECDHE

# nginx.confdaemon off;pid tmp/nginx.pid;error_log stderr debug;events { }http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl;        # SSL 协议版本        ssl_protocols TLSv1.2;        # 证书        ssl_certificate ssl/rsa.pem;        # 私钥        ssl_certificate_key ssl/rsa.key;        # ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;        ssl_ciphers AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256;        # 与False Start没关系,默认此项开启,此处减少抓包的干扰而关闭        ssl_session_tickets off;        return 200 "https ok \n";    }}


# nginx.confdaemon off;pid tmp/nginx.pid;error_log stderr debug;events { }http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl;        # SSL 协议版本        ssl_protocols TLSv1.2;        # 证书        ssl_certificate ssl/rsa.pem;        # 私钥        ssl_certificate_key ssl/rsa.key;        ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;        # ssl_ciphers AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256;        # 与False Start没关系,默认此项开启,此处减少抓包的干扰而关闭        ssl_session_tickets off;        return 200 "https ok \n";    }}


TLS会话复用指客户端与服务端经过首次握手后,客户端内存中保存了服务端发过来的会话信息,后面的握手过程客户端携带上会话信息,服务端验证会话信息,验证通过直接恢复会话,跳过证书验证、密钥交换等握手过程。

根据服务端内存中是否保存客户端的会话信息,有两种会话复用技术:

  • Session Ticket: 服务端内存中不保存客户端会话信息,所有会话信息由客户端保存,类似于Cookie。TLS首次握手时,服务端用一个密钥将会话信息进行加密,通过New Session Ticket子协议发送给客户端,后续TLS握手时,客户端Client Hello携带Session Ticke,服务端用密钥对Session Ticket进行解密,若解密成功,则直接恢复会话。不再进行后续握手过程。Nginx中Session Ticket Key可以配置多个,或者不配置,由Nginx自行生成。



  • Session ID: 服务端内存保存各个客户端ID对应的会话信息,只将ID发送给客户端,类似于Session。TLS首次握手时,服务端生成客户端的ID及对应的会话信息,并将该ID发送给客户端。后续握手时,客户端Client Hello中携带ID,服务端通过ID查找内存中是否有该会话信息,找到则直接恢复会话。不再进行后续握手过程。



# Session IDssl_session_cache off | none | [builtin[:size]] [shared:name:size]# Session Ticketssl_session_tickets on | off# 密钥文件,服务器生成Session Ticket需要进行加密,验证时候需要解密。不指定服务器自行生成随机的密钥。# 密钥文件要求80字节的AES256密钥: openssl rand 80 > current.keyssl_session_ticket_key FILE;# 可以配置多个, 保证更换密钥,但不影响以前的解密,顺序从上到下。ssl_session_ticket_key current.keyssl_session_ticket_key previous.key# 设置Nginx可以重复使用session参数时间,包括Session ID, Session Ticketssl_session_timeout time;
daemon off;pid tmp/nginx.pid;error_log stderr debug;events { }http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl;        ssl_protocols TLSv1.2;        ssl_certificate ssl/rsa.pem;        ssl_certificate_key ssl/rsa.key;        ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;        # Session Ticket        # 可以配置多个密钥,这样做的目的是为了定期更换密钥,保证前向安全性。            # 将更换的密钥配置在上面,Nginx会优先采用上面的密钥进行加/解密,            # 若使用旧密钥加密的会话,上面的密钥解密不了,就使用后面的密钥进行解密。        ssl_session_tickets on;        ssl_session_ticket_key https://zhuanlan.zhihu.com/p/current.key;        ssl_session_ticket_key https://zhuanlan.zhihu.com/p/previous.key;        # Session ID        # ssl_session_cache shared:mysession:10m;        return 200 "https ok \n";    }}

首次握手

后续握手


daemon off;pid tmp/nginx.pid;error_log stderr debug;events { }http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl;        ssl_protocols TLSv1.2;        ssl_certificate ssl/rsa.pem;        ssl_certificate_key ssl/rsa.key;        ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;        # Session Ticket        ssl_session_tickets off;        # ssl_session_ticket_key https://zhuanlan.zhihu.com/p/current.key;        # ssl_session_ticket_key https://zhuanlan.zhihu.com/p/previous.key;        # Session ID        ssl_session_cache shared:mysession:10m;        return 200 "https ok \n";    }}

首次握手

后续握手


SSL/TLS涉及多种密码学算法,选择安全、且性能高的密码套件非常重要。但安全和性能是反相关的,需要平衡这两者。下面是选择建议:

  • 密钥协商算法:
  • ECDHE:优先选择ECDHE作为密钥协商算法,椭圆曲线选择x25519, 其次P-256
  • DHE: 长度选择2048bit
  • 身份验证算法
  • ECDSA:优先选择ECDSA,椭圆曲线选择x25519, 其次P-256
  • RSA:长度选择2048bit
  • 加密算法和分组模式
  • AES_128_GCM: 长度是128bit
  • CHACHA20_POLY1305
  • AES_256_GCM: 长度是256bit

TLSv1.2的密码套件非常多,但建议按照上面的进行选择,按上面的组合后:

  • ECDHE-ECDSA-AES128-GCM-SHA256
  • ECDHE-ECDSA-CHACHA20-POLY1305
  • ECDHE-RSA-AES128-GCM-SHA256
  • ECDHE-RSA-CHACHA20-POLY1305
  • ECDHE-ECDSA-AES256-GCM-SHA384
  • ECDHE-RSA-AES256-GCM-SHA384
  • DHE-RSA-AES128-GCM-SHA256
  • DHE-RSA-AES256-GCM-SHA384


TLSv1.3废除了许多不安全的密码套件, 只保留了以下5种密码套件

  • TLS_AES_128_GCM_SHA256
  • TLS_CHACHA20_POLY1305_SHA256
  • TLS_AES_256_GCM_SHA384
  • TLS_AES_128_CCM_SHA256
  • TLS_AES_128_CCM_8_SHA256

TLSv1.3中保留的算法

  • 密钥交换算法只保留:ECDHE, DHE
  • 椭圆曲线保留:x25519,P-256等5种
  • 加密算法只保留:AES,ChaCha20
  • 分组模式:GCM, Poly1305, CCM
  • 摘要只保留:SHA256, SHA384

废除密钥交换算法RSA, DH的原因是,这两种算法不具备前向安全性(今日收集,明日破解)

  • RSA:一旦密钥泄露,就可以破解出共享密钥,从而解密密文。
  • DH: 静态DH,使用相同的公钥和私钥来生成Pre-Master, DHE每次连接生成公钥,私钥,一次会话一次密码。
# 密码套件# 选择密码套件的顺序是从左到右ssl_ciphers ciphers;# 选择密码套件时,以客户端发送的密码套件的顺序为准,还是以服务端ssl_ciphers配置的顺序为准。# off: 以客户端优先级为准, 默认值# on: 以服务端优先级为准ssl_prefer_server_ciphers on | off# ECC椭圆曲线,不设置Nginx根据OpenSSL选择ssl_ecdh_curve X25519:P-256;# 若使用DHE密钥协商算法需要配置DHE Param, 不配会报错,DHE-Param要求2048bit# 可通过openssl dhparam -out tmp/ssl/dhparam.pem 2048 生成dhparam.pemssl_dhparam tmp/ssl/dhparam.pem;
daemon off;pid tmp/nginx.pid;error_log stderr debug;events { }http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl;        ssl_protocols TLSv1.2 TLSv1.3;        ssl_certificate ssl/ecc.pem;        ssl_certificate_key ssl/ecc.key;        ssl_ciphers TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_CCM_SHA256:TLS_AES_128_CCM_8_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;        ssl_prefer_server_ciphers on;        ssl_ecdh_curve X25519:P-256;        # Session Ticket        ssl_session_tickets on;        ssl_session_ticket_key https://zhuanlan.zhihu.com/p/current.key;        ssl_session_ticket_key https://zhuanlan.zhihu.com/p/previous.key;        # Session ID        # ssl_session_cache shared:mysession:10m;        return 200 "https ok \n";    }}

ssl_perfer_server_ciphers默认值,即off时,以客户端的密码套件顺序为准

daemon off;pid tmp/nginx.pid;error_log stderr debug;events { }http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl;        ssl_protocols TLSv1.2 TLSv1.3;        ssl_certificate ssl/ecc.pem;        ssl_certificate_key ssl/ecc.key;        ssl_ciphers TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305;        # ssl_prefer_server_ciphers on;        # Session Ticket        ssl_session_tickets on;        ssl_session_ticket_key https://zhuanlan.zhihu.com/p/current.key;        ssl_session_ticket_key https://zhuanlan.zhihu.com/p/previous.key;        # Session ID        # ssl_session_cache shared:mysession:10m;        return 200 "https ok \n";    }}

测试:

$ openssl s_client -connect www.xlhtec.com:443 -tls1_2 -cipher 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256' 2>&1 </dev/null | grep CipherNew, TLSv1.2, Cipher is ECDHE-ECDSA-CHACHA20-POLY1305    Cipher    : ECDHE-ECDSA-CHACHA20-POLY1305$ openssl s_client -connect www.xlhtec.com:443 -tls1_3 -ciphersuites 'TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256' 2>&1 </dev/null | grep CipherNew, TLSv1.3, Cipher is TLS_CHACHA20_POLY1305_SHA256

ssl_perfer_server_ciphers为on是,以服务端端的密码套件顺序为准

ssl_session_tickets on;

测试

$ openssl s_client -connect www.xlhtec.com:443 -tls1_2 -cipher 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256' 2>&1 </dev/null | grep CipherNew, TLSv1.2, Cipher is ECDHE-ECDSA-AES128-GCM-SHA256    Cipher    : ECDHE-ECDSA-AES128-GCM-SHA256

Strict-Transport-Security 是一个HTTP响应头部字段,用来告诉浏览器只能使用HTTPS访问服务器资源。

访问HTTPS网站时,若不指定https协议头,仍然会使用http进行请求。通常在服务端会使用301、302进行跳转到https。这样做虽然也可以最终让浏览器通过https访问服务器,但是存在两个问题:1. 首次访问时仍然时http,2.需要跳转浪费1-RTT的时间。

为了让浏览器访问时自动切换为HTTPS协议头,在服务端响应报文中增加头部字段Strict-Transport-Security即可,浏览器除了首次访问可能是http,后续将自动转为https进行访问。若首次访问都想让HTTP自动切换为HTTPS,可以加入HSTS Preload List(一个谷歌维护的列表,现在大部分主流浏览器都支持这个列表,这个列表直接告诉浏览器要用 HTTPS 访问的站点有哪些,所以在访问站点之前,浏览器先捞一遍这个列表,如果要访问的站点在这里面,就直接用 HTTPS 进行访问,所以即使是第一次访问,也会走 HTTPS 了。)

Strict-Transport-Security头部字段格式:

Strict-Transport-Security: max-age=<expire-time>Strict-Transport-Security: max-age=<expire-time>; includeSubDomainsStrict-Transport-Security: max-age=<expire-time>; preload
  • max-age= 设置在浏览器收到这个请求后的秒的时间内凡是访问这个域名下的请求都使用HTTPS请求。
  • includeSubDomains (可选) 如果这个可选的参数被指定,那么说明此规则也适用于该网站的所有子域名。
  • preload (可选)查看预加载 HSTS获得详情。不是标准的一部分。
# Nginx响应报文中增加头部字段语法# always: 无论响应状态码是多少,都要增加该头部字段add_header name value [always];# 增加 Strict-Transport-Security头部字段add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

TLSv1.2无论是False Start, Session ID, Session Ticket都只能将TLS握手缩短到1-RTT, 而TLSv1.3可以使用更激进的方法,将TLS握手过程缩短为0-RTT,不过现在大多数浏览器还不支持0-RTT。Firefox Nightly支持,可以用其测试。

  1. 首次握手过后客户端保存了服务端发过来的New Session Ticket,并且携带了early_data。客户端生成PSK
  2. 后续握手过程中带上PSK,和数据。服务端验证PSK正确。则接收数据,有点像TCP Fast Open



  1. TLSv1.3版本
  2. 配置了会话复用:Session ID 或 Session Ticket
  3. 浏览器支持。现在大多浏览器还不支持,我测试了Firefox Nightly是支持的

浏览器不支持的话,可以使用openssl命令测试

$ openssl s_client -connect www.xlhtec.com:443 -tls1_3 -keylogfile=https://zhuanlan.zhihu.com/p/tls13.log -sess_out=https://zhuanlan.zhihu.com/p/tls13.sess 2>&1  | grep 'Early data'$ openssl s_client -connect www.xlhtec.com:443 -tls1_3 -keylogfile=https://zhuanlan.zhihu.com/p/tls13.log -sess_in=https://zhuanlan.zhihu.com/p/tls13.sess -early_data=https://zhuanlan.zhihu.com/p/req.txt 2>&1 < /dev/null | grep 'Early data'
# 0-RTTssl_early_data on;# Session Ticketssl_session_tickets off;# ssl_session_ticket_key https://zhuanlan.zhihu.com/p/current.key;# ssl_session_ticket_key https://zhuanlan.zhihu.com/p/previous.key;# Session IDssl_session_cache shared:mysession:10m;

Nginx配置如下

daemon off;pid tmp/nginx.pid;error_log stderr debug;events { }http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl;        ssl_protocols TLSv1.2 TLSv1.3;        ssl_certificate ssl/ecc.pem;        ssl_certificate_key ssl/ecc.key;        ssl_ciphers TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_CCM_SHA256:TLS_AES_128_CCM_8_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;        ssl_prefer_server_ciphers on;        ssl_ecdh_curve X25519:P-256;        ssl_dhparam ssl/dhparam.pem;        # Session Ticket        ssl_session_tickets off;        # ssl_session_ticket_key https://zhuanlan.zhihu.com/p/current.key;        # ssl_session_ticket_key https://zhuanlan.zhihu.com/p/previous.key;        # Session ID        ssl_session_cache shared:mysession:10m;        # 0-RTT        ssl_early_data on;        location / {             return 200 "https ok \n";        }    }}

首次握手

后续握手实现0-RTT


HTTP/2是为了优化HTTP而诞生的协议,那么HTTP存在什么问题呢?

HTTP问题1:队首阻塞 HTTP单个TCP连接中,多个请求应答必须按照严格的顺序排队进行。比如服务器接收到多个请求报文,响应报文要按照请求报文发来时的先后顺序依次响应。这会造成队首阻塞问题,即第一个报文响应延迟,其他报文均需等待。为了解决此问题,浏览器通常打开多个TCP连接,但是这会造成服务连接过多,压力大。所以默认浏览器最多打开6个连接。

HTTP/2优化方法: HTTP/2为了兼容HTTP。报文语义上仍然由Header + Body组成,只是将每个请求/应答虚拟成一个Stream。然后将HTTP报文切割成几块,每一块封装成一个二进制帧。一个Stream中的所有二进制帧头部包含相同的Stream ID ,不同Stream中的二进制Stream ID不相同。然后一个TCP连接中多个Stream可以并发请求,服务器也可以并发应答。并且Stream之间相互独立,不存在先后顺序。从而解决了HTTP中队首阻塞的问题。但是一个Stream中的多个二进制帧必须严格按照顺序进行请求/应答。

HTTP问题2:头部字段占用空间大 HTTP报文中有非常多的头部字段,经常会出现一个请求报文中Body非常小,甚至没有,而Header仍然很大,就像大头娃娃。

HTTP/2优化方法: 虽然HTTP中也存在gzip压缩方法,但是该方法只是用来对Body进行压缩的,并没有对头部字段进行压缩。HTTP/2专门针对HTTP中的头部字段设计了HPACK压缩算法。该算法是一个有状态的算法。首先通信双方维护一张静态表,对常用的头部字段进行编号,这些编号称为Index,发送的报文中就只需要带上编号。接收方通过编号查找静态表从而恢复头部字段。由于编号比头部字段小得多,从而起到压缩头部字段的作用。若出现静态表中没有的字段,则会双方动态的将这些字段添加都静态表中。

  • HTTP/2目前都是基于TLS的,所以HTTP/2连接之前需要经过TCP三次握手,TLS握手。若服务端支持HTTP/2,则在握手的过程中,通过TLS扩展字段ALPN告知客户端升级为HTTP/2通信。
  • 对HTTP头部字段采用HPACK算法进行压缩,废除HTTP头部中起始行,将起始行中原来所要传递的信息编入头部字段中,这些字段称为伪头部字段,有别于原来的头部字段。比如:authority, :method:status分别表示域名,请求方法,状态码。
  • HTTP报文被拆分成多个二进制帧,虚拟出Stream的概念,对同一个Stream中的二进制帧携带相同的Stream ID,不同Stream的ID不能重复





  • 帧类型:分为数据帧,控制帧。比如SETTINGS, PING, PRIORITY帧是用来管理Stream的控制帧,而HEADRS, DATA帧属于数据帧
  • 流标识符:Stream ID

HTTP/2建立连接过程


  1. 首先进行TCP三次握手建立TCP连接
  2. SSL/TLS握手,握手过程中,若服务端支持HTTP/2协议,则在TLS子协议Encrypted Extensions扩展字段ALPN中携带h2
  3. 客户端需要回复一个特定的报文,称为Magic: PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n,该过程称为连接首页。后面双方就升级为HTTP/2协议进行通信。
  4. 双方可以在单个TCP连接中多个Stream并发请求/应答,称为多路复用。
listen 443 ssl http2;

Nginx配置文件

daemon off;pid tmp/nginx.pid;error_log stderr debug;events { }http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl http2;        ssl_protocols TLSv1.2 TLSv1.3;        ssl_certificate ssl/ecc.pem;        ssl_certificate_key ssl/ecc.key;        ssl_ciphers TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_CCM_SHA256:TLS_AES_128_CCM_8_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;        ssl_prefer_server_ciphers on;        ssl_ecdh_curve X25519:P-256;        # Session Ticket        ssl_session_tickets off;        # ssl_session_ticket_key https://zhuanlan.zhihu.com/p/current.key;        # ssl_session_ticket_key https://zhuanlan.zhihu.com/p/previous.key;        # Session ID        # ssl_session_cache shared:mysession:10m;        location / {             index index.html;            root /tmp/picture;            # return 200 "https ok \n";        }    }}

升级HTTP/2协议过程

多路复用,可以看出并行发送了多个请求,响应也是并行的。


TLS握手过程,客户端需要对证书进行验证,比如日期、域名、吊销状态。以前验证证书吊销状态使用CLR,CA将所有吊销的证书添加到一张黑名单中,客户端请求这张黑名单,匹配服务器证书是否在其中。不在则证书有效。这种方式随着吊销列表越来越大,请求该文件和匹配都变得非常低效。

后面出现OCSP(Oline Certificate Status Protocol)来解决该问题。客户端验证服务器证书只需携带证书的序列号Serial Number请求CA的OCSP服务器,该服务器返回使用自己私钥签名的证书状态。客户端用CA解压响应结果。 OCSP虽然高效,但还存在两个问题

  1. 他会泄露客户端的隐私,因为客户端都要携带证书Serial Number请求,OCSP服务器从而知道客户端想访问的网站
  2. 所有的客户端验证证书状态都要向OCSP发起请求,导致OCSP服务器压力倍增。

为了解决OCSP的问题,出现了OCSP stapling


# 开启OCSP stapling     ssl_stapling on | off;# Nginx是否需要验证OCSP服务器的响应(比如证书吊销状态)# 若配置为on就需要使用CA的公钥对响应结果解密。# 就需要使用ssl_trusted_certificate指定证书链, # 但若ssl_cetificate中已经包含了完整的证书链,就不用指定了。ssl_stapling_verify on | off;ssl_trusted_certificate FILE;# 若Nginx无法请求到OCSP服务器的响应,比如网络不好等等,也可以自己手动跟新响应,# 然后将响应保存到一个DER格式的文件中,然后指定该文件。ssl_stapling_file DER_FILE; # 自己指定OCSP服务,默认包含在证书中,不用指定。ssl_stapling_responder http://OCSP_SERVER;

Nginx配置文件

daemon off;pid tmp/nginx.pid;error_log stderr debug;events { }http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl http2;        ssl_protocols TLSv1.2 TLSv1.3;        ssl_certificate ssl/ecc.pem;        ssl_certificate_key ssl/ecc.key;        ssl_ciphers TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_CCM_SHA256:TLS_AES_128_CCM_8_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;        ssl_prefer_server_ciphers on;        ssl_ecdh_curve X25519:P-256;        ssl_dhparam ssl/dhparam.pem;        # Session Ticket        ssl_session_tickets off;        # ssl_session_ticket_key https://zhuanlan.zhihu.com/p/current.key;        # ssl_session_ticket_key https://zhuanlan.zhihu.com/p/previous.key;        # Session ID        ssl_session_cache shared:mysession:10m;        # 0-RTT        ssl_early_data on;        # OCSP stapling        ssl_stapling on;        location / {             return 200 "https ok \n";        }    }}

验证

$ openssl s_client -connect www.xlhtec.com:443 -status  < /dev/null 2>&1 | grep -i "OCSP response"OCSP response:OCSP Response Data:    OCSP Response Status: successful (0x0)    Response Type: Basic OCSP Response

Nginx支持多虚拟主机,并且可以实现每个虚拟主机配置不同的证书。我们知道只有完成SSL/TLS握手后才能进行HTTPS通信,而在TLS握手阶段,Server需要发送证书给客户端,那么配置了多虚拟主机不同证书的Server如何知道要发送哪一个证书呢?这就要感谢TLS 子协议Client Hello扩展字段SNI(Server Name Indication extension),该字段会携带服务器域名


每个虚拟主机证书不一样

http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl http2;            server_name www.xlhtec.com        ssl_protocols TLSv1.2 TLSv1.3;        ssl_certificate ssl/www_xlhtec_com_ecc.pem;        ssl_certificate_key ssl/www_xlhtec_com_ecc.key;        ...    }    server {         listen 443 ssl http2;            server_name blog.xlhtec.com        ssl_protocols TLSv1.2 TLSv1.3;        ssl_certificate ssl/blog_xlhtec_com_ecc.pem;        ssl_certificate_key ssl/blog_xlhtec_com_ecc.key;        ...    }}

若虚拟主机的证书都一样,就不用这么麻烦了,在http { }块中配置即可

http {     access_log tmp/access.log;    default_type text/html;    ssl_protocols TLSv1.2 TLSv1.3;    ssl_certificate ssl/ecc.pem;    ssl_certificate_key ssl/ecc.key;    server {         listen 443 ssl http2;            server_name www.xlhtec.com        ...    }    server {         listen 443 ssl http2;            server_name blog.xlhtec.com        ...    }}

一个虚拟主机可以配置多个证书,比如同时配置ECCDSA、RSA证书

http {     access_log tmp/access.log;    default_type text/html;    server {         listen 443 ssl http2;            server_name www.xlhtec.com        ssl_protocols TLSv1.2 TLSv1.3;        ssl_certificate ssl/ecc.pem;        ssl_certificate_key ssl/ecc.key;        ssl_certificate ssl/rsa.pem;        ssl_certificate_key ssl/rsa.key;        ...    }}

喔:语雀里面写了导入过来的,表格乱了,只能截图哈

http {     log_format main 'ssl_protocol: $ssl_protocol\n'                    'ssl_cipher: $ssl_cipher\n'                    'ssl_ciphers: $ssl_ciphers\n'                    'ssl_curves: $ssl_curves\n'                    'ssl_server_name: $ssl_server_name\n'                    'ssl_session_id: $ssl_session_id\n'                    'http2: $http2\n';    access_log tmp/access.log main;    ...}

日志输出

$ tail -f tmp/access.logssl_protocol: TLSv1.3ssl_cipher: TLS_AES_256_GCM_SHA384ssl_ciphers: TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA:DES-CBC3-SHAssl_curves: X25519:prime256v1:secp384r1:secp521r1:0x0100:0x0101ssl_server_name: www.xlhtec.comssl_session_id: 1d54d0e1009f9a25edb60f95951987762da4e8b3f483873db55fc95a4b1774f4http2: h2

    热门排行

    友情链接