TLS 与安全 入门
curl https://api.example.com/user/42 的 TCP 握手刚一完成, 客户端和服务器立刻跑第二次握手 —— TLS 1.3 握手 —— 来商定密码套件、 交换密钥、证明服务器身份、把连接切到加密记录。然后真正的 HTTP 请求才上线。 4 个 section 把画面搭起来:为什么有 TLS(机密性、完整性、真实性);密码学积木(对称、非对称、AEAD、ECDHE、HKDF);可交互的 TLS 1.3 握手走读; 以及 PKI —— 证书、链、CA、mTLS。最后是速查表。
为什么有 TLS —— 机密性、完整性、真实性
没 TLS,curl 到服务器路径上的任何人 —— 咖啡店 AP、楼里的路由器、 每个 ISP 跳点、机房交换机 —— 都能读和改每个字节。 TLS 一次性解 3 个问题,把它们搞混是最常见的安全错误。
2026 年 curl https://api.example.com/user/42 跑的时候, 从你笔记本出去的字节对任何抓线的人来说都是噪声。把 HTTPS 拔了 —— 改成普通http:// —— 同一个请求就变成明文抓包:URL、headers、cookies、 JSON body、响应。TLS 就是为防这个存在,但「加密」这个框架把它干的事说小了。
3 个保证
- 机密性 —— 加密对路径上任何被动观察者藏住 payload。 每连接一把密钥的 AES-128-GCM 意味着从线上抓字节的攻击者拿到的密文 在计算意义上与随机不可区分。
- 完整性 —— 主动攻击者不能在传输中翻转 bit 而不被发现。 AEAD(Authenticated Encryption with Associated Data)在密文边上产出每记录的 MAC; 翻一个 bit 解密就拒绝整条记录。这就是干掉敌对网络上「往响应里注入脚本」攻击的东西。
- 真实性 —— 客户端知道自己实际是在跟
api.example.com说话,不是冒充它的中间人。这是证书链的活:服务器证明自己持有客户端已经信任的某根 CA (直接或经中间证书)签过、且写着api.example.com的那张证书的私钥。
任何一个掉了,其他两个就意义不大。没完整性的机密性让攻击者改写加密字节去破坏明文 (padding-oracle 攻击的基础)。机密性 + 完整性没真实性让中间人能跟自己建一条完全加密的通道、 解密、转发、重加密 —— 「完美」加密给了错的端点。
TLS 在栈里的位置
TLS 层在 TCP 之上(或者 QUIC 里,等价物焊进传输层本身)。TCP 三次握手之后, 紧接着的包就是 TLS 握手,然后加密记录承载应用协议要送的任何东西 —— HTTP、IMAP、SMTP、AMQP、MQTT、gRPC。TLS 不知道也不在乎里面是什么; 它就是把双向字节流包进加密、完整性保护的记录里。
+-----------------------------+ | HTTP / IMAP / AMQP / gRPC | 应用协议(在这层明文) +-----------------------------+ | TLS record layer | 5 字节头 + AEAD 加密 payload +-----------------------------+ | TCP | 按序字节流 +-----------------------------+ | IP | 尽力而为的包 +-----------------------------+
记录层是 TLS 的下半:握手定好会话密钥后, 应用发的每个字节都被切成 ≤16 KB 的记录,小头(type + version + length)+ AEAD 加密 payload。序列号每连接隐式持有、喂进 nonce, 所以同一条记录发两次密文不同,接收方能发现重放。
「TLS 在哪里终止?」
终止 = 字节在哪解密。典型生产配置里,api.example.com解析到一个负载均衡器(Cloudflare、AWS ALB、Envoy 队列), 持有证书、跑 TLS 握手、解密请求,然后用以下 3 种协议之一跟实际应用服务器说话:
- 私网上明文 HTTP —— VPC 内常见;靠网络级隔离做安全。 一个错路由的包或一个被攻破的主机,你就全输了。
- 用宽松证书的重加密 TLS —— 内部 CA、长期证书。 LB 重加密到后端,所以字节在网上从不明文。
- 带服务身份的 mTLS —— 两侧通过证书互相认证;Section 04 讲。 现代 service mesh 的默认。
知道终止点重要,因为那是证书配的地方、TLS 版本策略强制的地方、 以及大多数 TLS 相关故障住的地方(「LB 上证书过期」是反复出现的事故主题)。
要点。「TLS 给 3 个保证:机密性(AEAD 加密藏住 payload)、 完整性(AEAD MAC 检测任何在途篡改)、真实性(证书链证明你在跟命名主机说话, 不是中间人)。记录层把任何 TCP 字节流 —— HTTP、IMAP、gRPC —— 包进 ≤16 KB 的加密记录。 TLS 通常在负载均衡器终止;后端流量根据信任模型是明文 HTTP、重加密 TLS 或 mTLS。」
密码学积木 —— 对称、非对称、AEAD、ECDHE
4 个想法,按序:对称密码(便宜、两侧要同一把密钥);非对称(贵、不需要共享秘密); AEAD(加密 + 完整性一个原语);密钥交换(在窃听者看不见的情况下你怎么拿到对称密钥)。 TLS 1.3 每样挑一个。
对称密码 —— 快速批量加密
对称密码用同一把密钥加密和解密。AES 是主力:128 位或 256 位密钥, 每个现代 CPU 都硬件加速(Intel AES-NI、ARM 密码扩展),每核轻松 5+ GB/s 吞吐。 TLS 1.3 的密码套件:
- TLS_AES_128_GCM_SHA256 —— AES-128 GCM 模式、HKDF 用 SHA-256。 带密码扩展的 x86 / ARM 上几乎到处的默认。
- TLS_AES_256_GCM_SHA384 —— AES-256、同形状。 某些合规体系要;比 AES-128 慢 ~30%,实际安全提升为零。
- TLS_CHACHA20_POLY1305_SHA256 —— ChaCha20 流密码 + Poly1305 认证器。 没 AES-NI 的 CPU(大多数旧手机、一些嵌入式)上比 AES 快。手机客户端经常偏好。
基本就是全名单。TLS 1.2 有 ~300 个密码套件;TLS 1.3 砍到一小撮经过审计的组合, 全部锁定 AEAD + ECDHE + HKDF。
非对称密码 —— 慢、公钥
非对称方案有密钥对:任何人都可以知道的公钥、只有主人有的私钥。 一个做的操作另一个撤销。用于签名(私钥签、公钥验)和密钥协商。它们比对称慢 ~1000 倍:
- RSA-2048 —— 证书签名和老部署里仍常见。RSA-1024 原则上破了(2030 左右)。 大多数情况下 RSA-4096 是杀鸡用牛刀。
- ECDSA P-256 —— 椭圆曲线签名,256 位密钥下大约 RSA-3000 位的安全。 小证书、快速验证,新服务器证书的现代默认。
- Ed25519 —— Edwards 曲线签名,还要更快,没参数歧义、没 nonce 重用陷阱。 SSH host key 用,一些根 CA 在采用。
- X25519 —— TLS 1.3 密钥交换用的椭圆曲线 Diffie-Hellman 变体 (注意:签名是 Ed25519、密钥交换是 X25519 —— 不同曲线)。
非对称操作只在握手时用 —— 每连接一次 RSA 解密或一次 ECDSA 签。 共享秘密一旦建好,之后每个字节都是对称的。
AEAD —— 加密 + 完整性一个原语
AEAD 之前的构造把加密和完整性分开做:encrypt-then-MAC、MAC-then-encrypt、encrypt-and-MAC。 实现不小心 3 种都有讨厌的失败模式:
# AEAD 之前启用的漏洞: BEAST(2011) —— TLS 1.0 CBC 模式 IV 可预测 → 恢复 cookie POODLE(2014) —— SSLv3 padding oracle;降级攻击暴露数据 LUCKY13(2013) —— MAC-then-encrypt CBC 里的时序侧信道 CRIME(2012) —— TLS 压缩 + 选择明文 → 恢复 headers
AEAD 把加密和完整性打包到一个原语里:一次调用接 (key、nonce、plaintext、associated data)、产出(ciphertext、tag)。 解密接(key、nonce、ciphertext、tag、associated data)、要么返回明文要么失败。 没「解密然后查 MAC」的序列可以搞砸。AES-GCM 和 ChaCha20-Poly1305 都是 AEAD。 TLS 1.3 把 AEAD 定为强制。
密钥交换 —— 对称密钥怎么诞生
两侧需要同一对称密钥,窃听者必须不能从线上看到的任何东西推出它。3 个历史方法:
- 静态 RSA 密钥交换(TLS 1.2 及更早,1.3 砍掉): 客户端挑一个随机 pre-master secret、用证书里服务器长期 RSA 公钥加密、送过去。 服务器用 RSA 私钥解密。没前向保密:任何之后拿到服务器 RSA 私钥的人, 都能解密用那张证书录下来的每次 TLS 会话。政府对手积极囤积加密流量正是为这个 —— 「现在收割、以后解密」。
- DHE —— Diffie-Hellman Ephemeral:两侧每连接挑一个随机秘密、 交换公开值、算共享秘密。服务器的长期密钥签名握手,但从不当作密钥交换密钥用 —— 所以之后偷它不能解密过去流量。前向保密。数学用大素数(2048+ 位),所以慢。 实现用太小素数有 Logjam 攻击漏洞。
- ECDHE —— 椭圆曲线 Diffie-Hellman Ephemeral:同想法,但在椭圆曲线上。 ~256 位密钥以远低代价给出等效于 3000 位 DH 的强度。TLS 1.3 强制 ECDHE; X25519 是通用默认曲线。
前向保密是关键属性:今天录密文、之后拿到服务器证书私钥的攻击者, 仍然不能解密录下的会话,因为临时密钥从未在任何地方写下来。 没前向保密,加密流量的耐久性只跟长期证书密钥的保密性一样。
HKDF —— 一个秘密变多个
ECDHE 共享秘密是 32 字节。TLS 1.3 需要很多密钥:握手加密密钥(server-write、client-write)、 应用流量密钥(server-write、client-write)、exporter secret(给 token binding 等)、 resumption secret(下一会话的 PSK)。HKDF(HMAC-based Key Derivation Function) 是标准原语:HKDF-Extract(salt, secret) 把熵压成固定大小 PRK、 然后 HKDF-Expand(PRK, info, len) 能被不同 info 字符串调用多次, 产出任意长度的独立密钥。输出密钥计算意义上独立 —— 攻破一个不暴露其他。
# TLS 1.3 实际派生的(简写): shared_secret = ECDH(client_x25519, server_x25519) handshake_secret = HKDF-Extract(salt = early_secret, secret = shared_secret) client_hs_traffic = HKDF-Expand-Label(handshake_secret, "c hs traffic", transcript_hash) server_hs_traffic = HKDF-Expand-Label(handshake_secret, "s hs traffic", transcript_hash) # ……继续到 master_secret → application keys → resumption_secret
要点。「对称密码(AES-128-GCM、ChaCha20-Poly1305)做批量数据加密 —— 快、两侧要同一把密钥。非对称(ECDSA、Ed25519、X25519)慢,只在握手时用于签名和密钥交换。 AEAD 把加密 + 完整性塞进一个原语;TLS 1.3 把它定为强制是因为 AEAD 之前的构造不断产生 padding-oracle 攻击。ECDHE 给前向保密:录密文 + 之后偷证书密钥仍然不能解密。 HKDF 把一个 ECDH 秘密拉伸成协议需要的所有独立密钥。」
TLS 1.3 握手 —— 一次往返到加密记录
TLS 1.2 需要 2 个 RTT 才能流第一个 HTTP 字节。TLS 1.3 只要 1 个。 客户端在 ClientHello 里投机性给出密码和 key share; 服务器确认并立刻加密剩余。省下的 —— 每连接 1 个 RTT —— 就是整个重写的全部动机。
我们 api.example.com 连接的 TCP 握手刚完成, 两侧有按序字节流但没加密。TLS 握手是线上下一件事, TLS 1.3 里它恰好装在一次往返里 —— 下面的步骤一条消息一条消息走它。
ClientHello 里投机性地送一份 key share。 服务器挑密码套件、送自己的 share、用 ECDH 算共享秘密, 立刻把握手剩余部分用握手流量密钥加密 —— 证书、CertificateVerify (对整段记录签名,所以降级攻击藏不住)、Finished。 应用记录走另一套通过 HKDF 派生的应用流量密钥。为什么 TLS 1.3 是 1 RTT、1.2 是 2 RTT
TLS 1.2 里,客户端发 ClientHello(提议密码)、 等 ServerHello + Certificate + KeyExchange + ServerHelloDone、 然后发自己的 ClientKeyExchange + Finished、 等服务器的 Finished —— 任何应用数据之前 2 个 RTT。 TLS 1.3 做了一个关键改动:客户端在第一条消息里就发 key share, 投机性挑一个它期望服务器也支持的密码。服务器同意的话,立刻派生共享秘密, ServerHello 回复能包含别的一切,已经加密。总握手:1 RTT。
TLS 1.2(2 RTT): TLS 1.3(1 RTT):
client -> ClientHello client -> ClientHello + key_share
server -> ServerHello server -> ServerHello + key_share +
-> Certificate {EE, Cert, CertVerify, Finished}
-> ServerKeyExchange client -> {Finished}
-> ServerHelloDone -> [Application Data]
client -> ClientKeyExchange
-> ChangeCipherSpec
-> Finished
server -> ChangeCipherSpec
-> Finished
client -> [Application Data]0-RTT —— 请求就搭第一个包
客户端如果之前连过这个服务器并保留了 resumption secret,能比 1 RTT 还快: 跟 ClientHello 同一个包里,客户端能在 PSK 派生密钥下夹带加密的「early data」。 总:0 RTT。HTTP 请求字面上就搭这个连接的第一个 IP 包。
坑:0-RTT 易重放。抓到 early-data 包的攻击者能之后重放 —— TLS 1.3 给 0-RTT 没有内建反重放(它需要服务器端状态,在 CDN 上扩展性差)。 所以 0-RTT 只对幂等请求安全。CDN 典型上对 GET 允许 0-RTT、 但对 POST/PUT/DELETE 强制 1-RTT 握手。
TLS 1.3 砍了什么(以及为什么)
最大的设计选择是负面的 —— 砍什么。TLS 1.3 删了曾产生过 CVE 的每个算法:
- 静态 RSA 密钥交换 —— 没前向保密(看 Section 02)。所有密钥交换必须是 (EC)DHE。
- MD5 和 SHA-1 在任何握手角色 —— 两个都有实用碰撞攻击。
- RC4 —— 偏的密钥流,够样本后能利用恢复明文。
- CBC 模式分组密码 —— BEAST、POODLE、LUCKY13 的来源。现在全是 AEAD。
- TLS 层压缩 —— 启用了 CRIME。应用层压缩(gzip)仍然没事, 因为攻击者不能像往 TLS 记录里那样往 HTTP body 里注入选择明文。
- 重协商 —— 被独立的 KeyUpdate 替代。重协商是 2009 年一个漏洞的来源, 那时服务器搞混两个客户端的数据。
净效果:TLS 1.2 的 ~300 个密码套件在 TLS 1.3 变成 ~5 个 —— 全部按构造安全。 不再有「在配置里禁用坏的」—— 坏的根本不存在。
记录完整性干掉降级攻击
CertificateVerify 消息是个签名,用服务器长期私钥, 签到目前为止每条握手消息的 SHA-256 哈希 —— 「transcript hash」。 攻击者要是篡改 ClientHello 强迫弱密码,服务器签的记录会跟客户端算的不同, 签名不会验过。这是对整族降级攻击(Logjam、FREAK)的防御。
也值得知道:Finished 消息是对记录的 MAC,用握手密钥, 所以两侧交叉检查算出了同样的 transcript hash。任何在途篡改打破 MAC、 在任何应用数据被送之前连接中止。
openssl 能看到的
$ openssl s_client -connect api.example.com:443 -tls1_3 -msg CONNECTED(00000005) >>> TLS 1.3, Handshake [length 00ff], ClientHello <<< TLS 1.2, Handshake [length 007a], ServerHello <<< TLS 1.3, Handshake [length 0024], EncryptedExtensions <<< TLS 1.3, Handshake [length 092e], Certificate <<< TLS 1.3, Handshake [length 0114], CertificateVerify <<< TLS 1.3, Handshake [length 0034], Finished >>> TLS 1.3, Handshake [length 0034], Finished --- Server certificate subject=CN = api.example.com issuer=C = US, O = Let's Encrypt, CN = R3 Cipher : TLS_AES_128_GCM_SHA256 Protocol : TLSv1.3 Verification: OK
要点。「TLS 1.3 是 1 RTT,因为客户端在 ClientHello 里投机性发 key share; 服务器挑密码、派生共享秘密、把证书 + CertificateVerify + Finished 都加密在 ServerHello 回复里送出。 客户端发 Finished;HTTP 能搭下一个包。0-RTT 用 PSK 跳过握手但易重放 —— 只用于幂等请求。 CertificateVerify 签整段记录,所以降级攻击(Logjam、FREAK)藏不住。」
PKI —— 证书、链、CA、mTLS
Section 02 的密码学建一条安全隧道;PKI 是告诉客户端隧道另一端是不是它要的主机的东西。 整座建筑 —— X.509 证书、信任根、ACME、撤销 —— 都为正确回答那一个问题存在。
X.509 —— 证书格式
证书是一个二进制结构(DER 编码的 ASN.1,通常显示为 PEM 包装的 Base64), 把公钥绑到一个或多个名字,由签发者签名。TLS 客户端实际看的字段:
- Subject —— 遗产「主人」名;
Common Name(CN)字段 以前装主机名。现代客户端(Chrome 2017 起)完全忽略 CN。 - Subject Alternative Name (SAN) —— 主机名的现代字段。 单张证书能列许多 SAN(
api.example.com、www.example.com、*.example.com通配)。客户端把请求的主机名跟这列表匹配。 - 公钥 —— 服务器的公钥(RSA-2048、ECDSA P-256 等)。 服务器用匹配的私钥签握手以证明所有权。
- 有效期 —— Not Before / Not After 时间戳。公共 CA(Let's Encrypt 等) 发 90 天证书;续签必须自动化。
- Issuer —— 链里上一级证书按名字。
- 扩展 —— Key Usage(digitalSignature、keyEncipherment)、 Extended Key Usage(serverAuth、clientAuth)、Basic Constraints(这张证书是 CA 吗?)、 CRL Distribution Points、OCSP responder URL、SCT 列表(Certificate Transparency)。
- 签名 —— 签发者对上面所有东西的签名,用签发者的私钥算。
$ openssl x509 -in cert.pem -noout -text | head -25
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 04:c5:ab:...
Signature Algorithm: ecdsa-with-SHA256
Issuer: C = US, O = Let's Encrypt, CN = R3
Validity
Not Before: Mar 1 00:00:00 2026 GMT
Not After : May 30 23:59:59 2026 GMT
Subject: CN = api.example.com
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
EC Public Key: (256 bit)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Subject Alternative Name:
DNS:api.example.com, DNS:www.example.com链 —— 为什么我们需要中间证书
典型的链深 3 张:
Leaf: CN = api.example.com (R3 签发,有效 90 天)
|
| R3 的私钥签
v
Intermediate: CN = Let's Encrypt R3 (ISRG Root X1 签发,有效 ~5 年)
|
| ISRG Root X1 的私钥签
v
Root: CN = ISRG Root X1 (自签名,有效 ~30 年)
^
| 每个浏览器 / OS 信任仓预装为什么要中间?风险隔离。根 CA 的私钥是那个 CA 世界上最珍贵的密钥 —— 离线保管,在 HSM,在安全设施。它只签中间证书,从不离开。中间证书的密钥被攻破时, CA 撤销那个中间证书(传播要花力气)、发新的 —— 但根保持安全。 根被攻破会要每个浏览器和 OS 装新信任仓。根每 10-20 年轮一次;中间证书更频繁。
握手时,服务器送 leaf 证书和它知道的任何中间证书。客户端走链, 用下一张证书的公钥验每个签名,直到到达一个签发者在本地信任仓里的证书。
ACME 和 Let's Encrypt
2016 之前,拿证书意味着在 CA 网站填表、付 100 美元+/年、手动装文件。ACME(Automated Certificate Management Environment、RFC 8555) 是把这一切自动化的协议。Let's Encrypt 是规模化跑 ACME 的免费 CA。流程:
- 你的
certbot/acme.sh/ Caddy / Traefik 生成新密钥对。 - 它请 Let's Encrypt 给
api.example.com发证书。 - Let's Encrypt 挑战客户端证明对域名的控制:在
http://api.example.com/.well-known/acme-challenge/xyz提供特定文件 (HTTP-01),或设 DNS TXT 记录(DNS-01)。 - Let's Encrypt 取挑战来验证,然后签发并返回证书(90 天有效)。
- 你的工具装证书并在 ~60 天时安排续签任务。
对行业的影响:HTTPS 部署从 2014 年页面加载的 ~30% 到 2026 年的 ~95%。 EV(Extended Validation、「绿条」)和 OV(Organization Validation)证书基本死了, 因为 Chrome 2019 年起不再显示绿条 —— 用户反正不能可靠区分 DV 和 EV、 EV 没实际安全好处。
撤销 —— 证书需要被杀死时
证书能发 90 天但第 7 天被攻破。客户端怎么知道?3 个机制,都不完美:
- CRL(Certificate Revocation List)—— CA 发布一份签了名的撤销序号列表。 原始方法;列表变巨大(MB 级),下载代价占主导。浏览器基本不再检查。
- OCSP(Online Certificate Status Protocol)—— 客户端给 CA 的 OCSP responder 发实时 HTTP 请求问「序号 X 还有效吗?」每证书加一个 RTT、把浏览历史漏给 CA、 responder 挂了浏览器要么 soft-fail(不安全)要么 hard-fail(拒绝)。 实际中基本 soft-fail。
- OCSP stapling —— 服务器周期性从 CA 取新鲜 OCSP 响应、 在 TLS 握手时跟证书一起提供。客户端拿到新鲜性保证、没额外 RTT、没隐私泄漏。 现代默认。
还有:Certificate Transparency (CT) —— 每张公共信任证书签发前必须记录到 append-only CT 日志里。浏览器拒绝没足够 SCT(Signed Certificate Timestamp)的证书。 这让误发公开:CA 给你不要的域名发证书,你能在日志里看到并投诉。 CT 干掉了 2015 年 Symantec 误发假 google 证书的丑闻循环。
mTLS —— 客户端也有证书时
标准 TLS 里,只有服务器出示证书。双向 TLS(mTLS)要求客户端也出示一张 —— 两侧互相认证。证书链验证两个方向都跑。
用例:
- 服务网格里的服务到服务 —— Istio、Linkerd、Consul Connect 给每个内部 RPC 默认 mTLS。代理 sidecar 处理证书签发、轮换、验证;应用看到的就是普通 HTTP 或 gRPC 调用。
SPIFFE/SPIRE把身份模型标准化:每个工作负载拿到一个 SPIFFE ID, 像spiffe://prod/payment-service,嵌在其证书里。 - 高安全端点的 API 认证 —— 银行、政府、B2B 集成。 客户端的证书比任何 API key 更强地标识它(不能像 API key 那样通过日志文件外泄)。
- VPN 替代(零信任网络) —— Cloudflare Access、Tailscale、Google BeyondCorp。 每台设备拿证书;访问由证书验证而不是网络位置门禁。
证书 pinning —— 连 PKI 都不够时
高价值手机 app(银行、即时通讯),标准信任仓太宽 —— 几百个 CA、任何一个都能给你的域名发欺诈证书 (CT 让这个公开但不阻止它)。Pinning 在 app 里硬编码期望的证书 (或其公钥 SPKI 哈希);握手时出示的证书不匹配,无论信任仓说什么,连接都被拒绝。
浏览器用 HPKP(HTTP Public Key Pinning)试过这个但 2018 年弃用 —— 丢了 pin 的密钥太容易把自己网站砖死。手机 pinning 仍常见,因为 app 厂商控制更新渠道、 能下发新 pin。警告:漏轮换能把用户锁外,要紧急 app 更新。
要点。「证书把公钥绑到主机名(在 SAN 字段),由中间 CA 签名、 由客户端信任仓里的根 CA 签名。leaf 通过 ACME / Let's Encrypt 每 90 天轮一次; 根每 10 年轮一次。实际撤销是 OCSP stapling + Certificate Transparency。 mTLS 加客户端证书认证 —— 内部 RPC 的服务网格默认、零信任 VPN 替代。 Pinning 给高价值手机 app 硬编码期望的证书。」
速查表
6 道值得能冷讲清楚的核心问题、5 个 code review 时一眼看出来的红旗。
TLS 提供哪 3 个保证、各自怎么提供?
机密性 —— AEAD 加密(AES-128-GCM 或 ChaCha20-Poly1305)对任何被动观察者藏住 payload。 完整性 —— 同一个 AEAD 原语产出每记录的 MAC;任何在途篡改让解密失败。 真实性 —— 证书链证明服务器持有命名所请求主机的证书的私钥、 那张证书(传递地)由客户端信任仓里的根签名。任一掉了其他基本没用: 没真实性让 MITM 跑一条对自己的完美加密通道。
为什么 RSA 密钥交换(vs ECDHE)被认为是遗产?
没前向保密。静态 RSA 密钥交换里,客户端用服务器长期 RSA 公钥加密 pre-master secret。 任何之后拿到 RSA 私钥的人能解密用那张证书录过的每次 TLS 会话 —— 这就是促使国家级对手抓流量的「现在收割、以后解密」威胁模型。 ECDHE 每连接生成临时密钥用完即丢;之后偷长期证书密钥不暴露它。 TLS 1.3 完全砍掉静态 RSA。
把 TLS 1.3 握手走一遍 —— 每条消息里有什么?
ClientHello:支持的密码、X25519 公钥、SNI = 主机名。ServerHello:选定的密码、 服务器 X25519 公钥(现在两侧都能算共享 ECDH 秘密、通过 HKDF 派生握手密钥)。 然后,在握手密钥下加密:EncryptedExtensions、Certificate(leaf + 中间证书)、 CertificateVerify(对整段握手记录的签名 —— 干掉降级攻击)、Finished(记录的 MAC)。 客户端验链、加密发送自己的 Finished。两侧切到应用流量密钥。HTTP 搭下一个包。 总:app data 之前 1 RTT。
「验证证书链」是什么意思 —— 浏览器实际在查什么?
按序 5 件事:(1)Subject Alternative Name 列表包含被连主机名。 (2)当前时间在 Not Before / Not After 之间。(3)Extended Key Usage 包含 serverAuth。 (4)链里每张证书,上一级证书的公钥成功验它的签名;最顶层证书的签发者匹配本地信任仓里的某证书。 (5)通过 OCSP staple(或 soft-fail OCSP)的撤销检查、CT 日志的足够 SCT 在场。 任一步失败硬错误中止握手 —— 没「忽略并继续」。
0-RTT 是什么、为什么危险?
0-RTT(early data)让复用前会话 PSK 的客户端在第一个包里跟 ClientHello 一起送加密的应用数据 —— 完全跳过握手。危险:数据易重放。抓到 0-RTT 包的攻击者能之后重放; TLS 1.3 给 early data 没内建反重放(它要服务器端状态、在 CDN 上扩展性差)。 只对幂等请求安全;CDN 典型上对 GET 允许 0-RTT、POST / PUT / DELETE 强制 1-RTT。
mTLS 是什么、你在哪用它?
双向 TLS —— 客户端也出示证书,服务器像客户端验服务器一样验客户端的链。主要用法: 服务网格里的服务到服务认证(Istio / Linkerd / Consul Connect、常通过 SPIFFE 身份)、 API key 太弱的高安全 API 认证(银行、B2B 集成)、以及替代 VPN 的零信任网络访问 (Cloudflare Access、Tailscale)。网格用例最常见 —— mTLS 是任何现代 Kubernetes 部署里 东西流量的默认。
Code review 红旗
- 生产 HTTP 客户端禁用证书验证。
curl -k、requests.get(..., verify=False)、InsecureSkipVerify: true。 这把 TLS 变成「无 mTLS 的 mTLS」:加密给某个服务器、没办法发现 MITM。 基本总是有人撞到证书错误、绕过去而不是修它的信号。 - HTTP + 应用级 AES「加密」而不用 TLS。自己卷密码学意味着自己卷 padding-oracle / 重放 / nonce 重用 bug。 TLS 是 10 年专业密码学家的产出;用它,把你的应用逻辑放上面而不是旁边。
- 公共服务上启用旧 TLS 版本(1.0 / 1.1)。BEAST、POODLE、LUCKY13 都住那。现代浏览器默认拒绝 1.0 / 1.1, 但服务器端兼容标志经常仍允许。显式禁用:nginx 里
ssl_protocols TLSv1.2 TLSv1.3。 - 自签名证书没内部 PKI / 信任分发。意味着每个客户端要么禁了验证跑(看红旗 1)、要么在代码里 pin 一份证书副本(轮换噩梦)。 架个小内部 CA(smallstep、Vault PKI)、通过配置管理分发根证书。
- 非幂等端点启用 0-RTT。允许 0-RTT POST 让攻击者能重放之前抓的购买、转账、DB 改动请求。 大多数 CDN 让你按路由白名单 0-RTT —— 限在 GET 和 HEAD、绝不要改动动词。