公网环境使用 HTTPS 参考:使用 Certbot 获取 Let’s Encrypt 颁发的 TLS 证书 | ZYF.IM

简介

HTTPS(HyperText Transfer Protocol Secure)是在 HTTP 之上通过 TLS/SSL 实现加密的安全通信协议,用来保护浏览器与服务器之间的数据机密性、完整性与身份可信性。

开始实验

mkcert 会在本地生成一套根 CA(包含根证书和私钥),并把根证书导入系统/浏览器的信任列表。随后,它用这套 CA 的私钥为 localhost 等域名签发服务器证书;因为根证书已被信任,浏览器访问时就会把服务器证书视为可信。

brew install mkcert
mkcert -install
mkcert localhost
# The certificate is at "./localhost.pem" and the key at "./localhost-key.pem" ✅
# vi default.conf
server {
    listen 443 ssl;

    server_name localhost;
    ssl_certificate /etc/ssl/certs/localhost.pem;
    ssl_certificate_key /etc/ssl/certs/localhost-key.pem;

    # 只允许 TLSv1.2 和 TLSv1.3 协议,显示禁止 TLSv1.0 和 TLSv1.1 协议
    ssl_protocols TLSv1.2 TLSv1.3;
    # 只保留高强度、带身份验证、且不使用 MD5 的 TLS 1.2(及以下)套件
    ssl_ciphers HIGH:!aNULL:!MD5;
    # 优先使用服务器端加密套件
    ssl_prefer_server_ciphers on;

    location / {
        root /usr/share/nginx/html;
        index  index.html index.htm;
    }
}
docker run -d --name nginx-ssl \
  -p 8443:443 \
  -v ./default.conf:/etc/nginx/conf.d/default.conf:ro \
  -v ./localhost.pem:/etc/ssl/certs/localhost.pem:ro \
  -v ./localhost-key.pem:/etc/ssl/certs/localhost-key.pem:ro \
  nginx:alpine

# docker rm -f nginx-ssl
# docker container restart nginx-ssl

测试

游览器

游览器访问 https://localhost:8443/,如果证书是绿色的,说明成功了。

curl

curl -I https://localhost:8443/

HTTP/1.1 200 OK
Server: nginx/1.29.0
Date: Fri, 27 Jun 2025 01:54:36 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 24 Jun 2025 17:57:38 GMT
Connection: keep-alive
ETag: "685ae712-267"
Accept-Ranges: bytes
# -v 显示详细信息,查看 TLS 版本
curl -I https://localhost:8443/ -v

...
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF
...

深入探索

HTTP 的三大安全缺陷

网络早已布满监听、篡改与伪装。

  • 明文传输。任何中间节点(Wi-Fi 热点、ISP、CDN、政府网关)都能直接读取用户名、密码、Cookie、表单内容等敏感信息。
  • 缺少完整性保护。中间人可随意插入、删除或修改页面内容(广告注入、恶意脚本、暗链植入)。客户端亦无法察觉。
  • 无法验证身份。客户端无法确认自己连接的是"真正的 server"。攻击者可通过 DNS 欺骗、ARP 攻击或流量劫持将流量导向钓鱼站点。

为什么需要 HTTPS

TLS 通过“加密 + 身份认证 + 完整性校验”应对上述风险。

  • 加密(Confidentiality)。对称加密保护数据内容;密钥通过非对称算法安全协商。
  • 数据完整性(Integrity)。MAC(消息鉴别码)或 AEAD 算法确保内容未被篡改。
  • 身份认证(Authentication)。服务器出示数字证书;浏览器验证证书链与域名匹配。

MAC vs AEAD

  • MAC 提供数据完整性与对称实体认证,需要与独立的加密步骤结合来保障机密性。
    • Message Authentication Code
    • 只负责“认证”,不加密数据;与加密算法解耦。典型实现是 HMAC-SHA1/HMAC-SHA256。
  • AEAD 将“加密 + 认证”合二为一,接口更简单、实现更安全、性能更佳,是现代协议的首选。
    • Authenticated Encryption with Associated Data
    • 加密数据 + 认证数据
    • 典型实现是 AES-GCM/ChaCha20-Poly1305
  • 在 TLS/QUIC/Noise 等现代协议中,AEAD 已全面取代传统的 “MAC-then-Encrypt”。

证书类型

按层级角色:

  • 根证书 (Root CA):最高级别,Subject = Issuer 自签名,预装在操作系统/浏览器的“根信任库”内,用于验证下级证书。
  • 中级证书 (Intermediate CA):由根证书签名,用于验证下级证书。降低根密钥泄露风险。
  • 最终实体证书 (End Entity Certificate):由中级证书签名,绑定具体域名或主体,用于验证服务器身份。

按验证级别:

  • DV(Domain Validation)证书仅验证域名所有权,通过自动化流程颁发,价格低廉甚至免费(如 Let’s Encrypt)。
  • OV(Organization Validation)证书需要验证组织实体信息,在浏览器证书详情中可查看公司名称。
  • EV(Extended Validation)证书验证最为严格,地址栏会显示公司名称,但多数现代浏览器已弱化此功能的 UI 展示。

证书链

在 HTTPS、TLS、S/MIME 等基于 X.509 的公钥基础设施 (PKI) 中,任何一张服务器或用户证书都必须被“信任根 (Root CA)”间接或直接签名。 从根证书一路向下到最终实体证书所形成的“逐级签名”路径,就称为证书链,也叫信任链 (Chain of Trust)。

HTTPS 在网络堆栈中的位置

  1. HTTP/HTTPS 仍是应用层协议。
  2. TLS(Transport Layer Security)位于 HTTP 与 TCP 之间;它把上层的明文字节(HTTP 报文)加密后再交给 TCP。
  3. TCP 提供可靠的字节流;IP 负责寻址和路由;最底层是物理/链路层。
  4. HTTPS = HTTP over TLS over TCP over IP。
协议主要职责典型端口
HTTP应用语义:URL、方法、头、状态码…80(无加密)
TLS机密性、完整性、身份认证443(惯例)
TCP可靠传输、流量控制、重传任意 (443)
IP路由、分片、寻址
  1. HTTPS ≠ 新协议。它只是“HTTP 报文透过 TLS 隧道运输”的俗称。协议栈中依然能看到标准的 HTTP 语法,只是先被 TLS 加密,运输途中看不到明文。
  2. TLS 与 TCP 的独立性。TLS 不关心丢包、重传;这些由 TCP 处理。相反,TCP 不理解证书、握手;这些由 TLS 负责。

TLS 握手过程

客户端                           服务器
   |<--- SYN → SYN/ACK → ACK --->| 建立可靠的 TCP 连接

   |--- ClientHello (TLS)    --->| 浏览器发起 TLS 握手,告诉服务器自己支持的协议版本、密码套件、随机数(Client Random)等信息
   |<-- ServerHello + 证书     ---|
   |--- 密钥协商/Finished ------->|
   |<-- Finished ----------------|

   TLS 信道建立完成
   |--- HTTP GET / ------------->|
   |<-- HTTP/1.1 200 OK ---------|
sequenceDiagram
  participant Client
  participant Server
  Client->>Server: ClientHello<br/>– 支持的 TLS 版本、随机数、加密套件
  Server-->>Client: ServerHello<br/>– 选定版本/套件、随机数
  Server-->>Client: Certificate
  Server-->>Client: ServerHelloDone
  Client->>Server: ClientKeyExchange<br/>– 预主密钥 (用公钥加密)
  Client->>Server: ChangeCipherSpec
  Client->>Server: Finished (用对称密钥加密)
  Server-->>Client: ChangeCipherSpec
  Server-->>Client: Finished
  note over Client,Server: 之后开始对称加密的数据传输
  • 双方首先协商算法与随机数;
  • 客户端用服务器公钥加密预主密钥;
  • 双方基于随机数与预主密钥生成会话密钥;
  • 后续通信全部使用对称加密,提高效率。

TLS 1.3

TLS 1.3(RFC 8446)是在 2018 年 8 月正式发布的最新版本,相比 TLS 1.2 做了深度“瘦身+加速+强化安全”。

  • TLS 1.3 把握手变成 1-RTT 并引入 0-RTT,显著降低延迟。
  • 强制 ECDHE + AEAD + SHA-2,淘汰所有过时与高危算法,默认提供前向保密。
  • 握手更多字段被加密,降低被动监听价值,同时带来运维与抓包新挑战。
  • 配置侧仅需保留少量推荐套件 (TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256 等),比 TLS 1.2 更易运维。

RTT vs. 0-RTT

“RTT” 本身确实是一个时间量(Round-Trip Time),指数据包从客户端发出、到达服务端,再把响应返回客户端所经历的往返延迟,常用 毫秒 (ms) 表示。

在协议说明里看到的 “1-RTT 握手 / 2-RTT 握手” 属于约定俗成的缩写:把 “一次往返时间” 抽象成 “一次来回(round trip)” 这个计数单位来用。

证书与密钥

openssl version
# 看看证书
openssl x509 -in localhost.pem -noout -text

证书的结构:

Certificate:
|- Data:
  |- Version: 3
  |- Serial Number: ...
  |- Signature Algorithm: sha256WithRSAEncryption
  |- Issuer: O=mkcert development CA, OU=..., CN=mkcert ...
  |- Validity:
    |- Not Before: Jun 27 03:39:39 2025 GMT
    |- Not After : Sep 27 03:39:39 2027 GMT
  |- Subject: O=mkcert development CA, OU=...
  |- Subject Public Key Info: <网站的公钥>
    |- Public Key Algorithm: rsaEncryption
      |- Public-Key: (2048 bit)
      |- Modulus: <模数>
      |- Exponent: 65537 (0x10001)
   |- X509v3 extensions
|- Signature Algorithm: sha256WithRSAEncryption
|- Signature Value: <颁发机构(CA)用自己私钥算出的数字签名,浏览器用 CA 公钥验证>

在 RSA 体系里,公钥由两部分组成:

  • n:模数 (Modulus)
  • e:公钥指数 (Exponent)
# 导出公钥
openssl x509 -in localhost.pem -pubkey -noout > pubkey.pem
# 查看导出的公钥
openssl pkey -pubin -in pubkey.pem -text -noout
# 看看私钥里有什么
openssl rsa -in localhost-key.pem -text -noout

私钥的结构:

Private-Key: (2048 bit, 2 primes)
# 模 n = p × q
modulus: ...
# e 公钥指数
publicExponent: 65537 (0x10001)

# m = c^d mod n
privateExponent: ...

# p 和 q 是两个大质数
prime1: ...
prime2: ...

# exponent1 = d mod (p-1)
exponent1: ...
# exponent2 = d mod (q-1)
exponent2: ...
# coefficient = inv(q) mod p
coefficient: ...

HTTP/2

当前使用的是 HTTP/1.1,如何启用 HTTP/2,请移步 NGINX 启用 HTTP/2 | ZYF.IM

HTTP/3

请移步 NGINX 启用 HTTP/3 | ZYF.IM

References

– EOF –