协议与应用基础(三):HTTPS、证书链与中间人攻击
-
- 前言
- [一、HTTPS 保护的到底是什么](#一、HTTPS 保护的到底是什么)
-
- [1.1 HTTPS 的协议栈位置](#1.1 HTTPS 的协议栈位置)
- [1.2 HTTPS 的三个核心目标](#1.2 HTTPS 的三个核心目标)
- [二、从输入 URL 到 TLS 握手](#二、从输入 URL 到 TLS 握手)
-
- [2.1 DNS 与 HTTPS 的关系](#2.1 DNS 与 HTTPS 的关系)
- [2.2 SNI 和 ALPN](#2.2 SNI 和 ALPN)
- [三、X.509 证书到底声明了什么](#三、X.509 证书到底声明了什么)
-
- [3.1 证书的抽象模型](#3.1 证书的抽象模型)
- [3.2 Subject Alternative Name](#3.2 Subject Alternative Name)
- [3.3 Key Usage 与 Extended Key Usage](#3.3 Key Usage 与 Extended Key Usage)
- [3.4 有效期、序列号和撤销](#3.4 有效期、序列号和撤销)
- 四、证书链是怎样建立信任的
-
- [4.1 根证书是信任锚](#4.1 根证书是信任锚)
- [4.2 中间 CA 和终端证书](#4.2 中间 CA 和终端证书)
- [4.3 为什么服务器通常发送中间证书](#4.3 为什么服务器通常发送中间证书)
- [4.4 交叉签发与多条路径](#4.4 交叉签发与多条路径)
- [五、HTTPS 握手中证书、签名与 ECDHE 的分工](#五、HTTPS 握手中证书、签名与 ECDHE 的分工)
-
- [5.1 证书:绑定长期身份公钥](#5.1 证书:绑定长期身份公钥)
- [5.2 ECDHE:建立本次会话的共享秘密](#5.2 ECDHE:建立本次会话的共享秘密)
- [5.3 CertificateVerify:证明私钥持有](#5.3 CertificateVerify:证明私钥持有)
- [5.4 Finished:确认双方看到同一个握手](#5.4 Finished:确认双方看到同一个握手)
- 六、中间人攻击是怎样发生的
-
- [6.1 裸 DH/ECDH 的中间人攻击](#6.1 裸 DH/ECDH 的中间人攻击)
- [6.2 HTTPS 如何阻止这种攻击](#6.2 HTTPS 如何阻止这种攻击)
- [6.3 复制证书不等于复制身份](#6.3 复制证书不等于复制身份)
- [6.4 伪造域名证书](#6.4 伪造域名证书)
- [七、企业 HTTPS 代理为什么能够解密流量](#七、企业 HTTPS 代理为什么能够解密流量)
-
- [7.1 企业根证书模型](#7.1 企业根证书模型)
- [7.2 为什么普通恶意软件不能简单照搬](#7.2 为什么普通恶意软件不能简单照搬)
- [7.3 证书固定的边界](#7.3 证书固定的边界)
- [八、如何排查 HTTPS 证书和中间人问题](#八、如何排查 HTTPS 证书和中间人问题)
-
- [8.1 先看当前连接的证书](#8.1 先看当前连接的证书)
- [8.2 浏览器显示证书错误时](#8.2 浏览器显示证书错误时)
- [8.3 观察证书是否被替换](#8.3 观察证书是否被替换)
- [8.4 CTF 中的常见考点](#8.4 CTF 中的常见考点)
- 九、常见误区
-
- [9.1 HTTPS 可以隐藏 IP 和域名](#9.1 HTTPS 可以隐藏 IP 和域名)
- [9.2 有证书就一定是目标网站](#9.2 有证书就一定是目标网站)
- [9.3 自签名证书一定不安全](#9.3 自签名证书一定不安全)
- [9.4 中间人只能发生在 HTTP 明文连接](#9.4 中间人只能发生在 HTTP 明文连接)
- [9.5 证书固定可以替代所有 PKI](#9.5 证书固定可以替代所有 PKI)
- 十、总结
前言
HTTPS 并不是一种新的应用层协议,而是 HTTP over TLS:先使用 TLS 建立一条经过认证和加密的连接,再在这条连接上传输 HTTP 请求和响应。
很多人第一次接触 HTTPS 时,会把它简单理解成:
浏览器拿到服务器证书,验证证书,然后把 HTTP 加密起来。
这句话方向没有错,但省略了几个决定安全性的关键问题:
- 证书链到底验证了什么;
- 浏览器为什么信任某个 CA;
- 证书中的域名如何与当前访问地址匹配;
- TLS 握手中的签名和 ECDHE 分别负责什么;
- 攻击者如何进行中间人攻击;
- 为什么"证书签名验证成功"仍然可能不安全;
- 企业代理、抓包工具和恶意根证书为什么可以解密 HTTPS。
本文以浏览器访问 HTTPS 网站为主线,串起 DNS、TCP、TLS、X.509 证书、证书链、主机名校验和中间人攻击。重点不是记住某个命令,而是理解浏览器究竟在验证什么,以及攻击者必须突破哪些环节才能冒充目标网站。
本文用于学习、授权测试和 CTF 分析。不要在未授权的网络、设备或账号上实施中间人攻击、证书替换或流量解密。
现代密码学 专栏:https://blog.csdn.net/r_feynman_/category_13190241.html
Crypto 密码解析实战靶场:https://blog.csdn.net/r_feynman_/category_13194584.html
一、HTTPS 保护的到底是什么
1.1 HTTPS 的协议栈位置
访问:
text
https://example.com/path
通常会经历以下层次:
text
HTTP
↓
TLS
↓
TCP
↓
IP
HTTP 负责请求和响应语义;TLS 负责身份认证、密钥协商、握手完整性以及应用数据保护;TCP 负责可靠字节流传输。
HTTPS 不是把 HTTP 消息单独加密后发送,而是先建立 TLS 会话,再把 HTTP 字节流交给 TLS 记录层。连接建立后,攻击者通常仍然可以观察:
- 通信双方的 IP 地址;
- 连接时间和流量大小;
- 某些握手元数据;
- 访问行为的模式。
但在正确配置下,攻击者不能直接读取或修改 HTTP 请求路径、请求头和响应正文。TLS 也不自动隐藏所有流量分析信息。
1.2 HTTPS 的三个核心目标
机密性
网络观察者不能直接读取应用数据:
M → AEAD K C M\xrightarrow{\operatorname{AEAD}_K}C MAEADK C
其中 K K K 是本次 TLS 会话派生出的方向密钥。
完整性
攻击者修改密文、插入记录、删除记录或调整记录顺序时,接收方应检测到错误,而不能把篡改后的内容交给 HTTP 层。
服务器身份认证
客户端不仅要知道"对面有一把私钥",还要确认这把私钥对应的证书确实被授权给当前访问的域名。
这三者缺一不可。只有机密性没有身份认证,攻击者可以建立自己的加密连接;只有身份认证没有完整性,数据仍可能被篡改;只有完整性没有机密性,消息仍会泄露。
二、从输入 URL 到 TLS 握手
浏览器访问 HTTPS 网站时,可以粗略分成以下阶段:
- 解析 URL,确定主机名、端口和路径;
- 进行 DNS 解析,得到目标地址;
- 建立 TCP 连接,或在现代协议中建立 QUIC 连接;
- 发起 TLS ClientHello;
- 协商版本、密钥交换组、密码套件和扩展;
- 接收并验证服务器证书链;
- 验证服务器对当前握手的签名;
- 通过 ECDHE 或恢复 PSK 建立握手秘密;
- 使用 KDF 派生握手和应用数据密钥;
- 发送加密的 HTTP 请求和响应。
HTTPS 安全不是单个证书文件带来的,而是多个阶段共同完成的。DNS 被劫持、主机名校验被关闭、根证书被错误安装、TLS 库版本过旧,都可能改变最终安全结果。
2.1 DNS 与 HTTPS 的关系
DNS 通常负责把域名映射到 IP 地址,但 DNS 本身不等于 HTTPS 身份认证。即使攻击者把 example.com 解析到了错误的服务器,只要客户端严格验证证书中的名称,伪造服务器仍然不能通过 HTTPS 认证。
反过来,如果客户端关闭证书验证或只验证证书签名而不检查主机名,DNS 劫持就可能直接导向中间人服务。
DNSSEC、DoH 和 DoT 可以改善 DNS 数据的认证或传输隐私,但它们不能替代 TLS 证书验证。不同层次的安全机制应分别理解。
2.2 SNI 和 ALPN
TLS ClientHello 中常见两个扩展:
- SNI:告诉服务器客户端想访问哪个主机名;一台 IP 上托管多个 HTTPS 站点时,服务器据此选择证书和配置;
- ALPN:协商应用层协议,例如 HTTP/2 或 HTTP/1.1。
SNI 影响服务器选择证书,但客户端仍然必须验证收到的证书是否匹配目标主机名。ALPN 影响后续应用协议,不应被攻击者静默替换。
三、X.509 证书到底声明了什么
3.1 证书的抽象模型
一张证书可以抽象成:
Cert = Sign C A _ p r i v a t e ( 身份 , 公钥 , 有效期 , 用途 , 扩展 ) \operatorname{Cert}=\operatorname{Sign}_{CA\_private}(\text{身份},\text{公钥},\text{有效期},\text{用途},\text{扩展}) Cert=SignCA_private(身份,公钥,有效期,用途,扩展)
证书签名证明:
- TBS(待签名证书)内容没有被篡改;
- 签名者拥有对应 CA 私钥。
它不自动证明:
- 当前连接一定使用了证书对应的私钥;
- 证书中的组织永远可信;
- 当前主机名一定被允许;
- 应用层用户具有某种业务权限。
这些都需要其他验证步骤。
3.2 Subject Alternative Name
浏览器进行主机名验证时,重点检查 Subject Alternative Name(SAN)。如果目标是:
text
https://api.example.com
客户端需要判断该主机名是否匹配证书 SAN 中的 DNS 名称。通配符也有边界,例如:
text
*.example.com
通常只能匹配一个直接子域名,不能任意匹配 a.b.example.com。IP 地址应按照 IP 类型字段进行匹配,不能简单当作普通字符串。
不能只读取证书的 Common Name 或 Subject 文本就认为主机名验证完成。实际匹配规则还要处理大小写、国际化域名、通配符位置、尾随点和编码规范。
3.3 Key Usage 与 Extended Key Usage
证书中的用途限制同样重要:
digitalSignature允许用于数字签名;keyEncipherment与某些密钥加密模式有关;keyAgreement允许密钥协商;keyCertSign表示可用于签发证书;serverAuth表示可用于 TLS 服务器认证;clientAuth表示可用于 TLS 客户端认证。
服务器证书即使链验证成功,如果 EKU 不允许服务器认证,客户端也不应接受它作为普通 HTTPS 服务器证书。
3.4 有效期、序列号和撤销
客户端至少应检查:
N o t B e f o r e ≤ 当前时间 ≤ N o t A f t e r NotBefore\le\text{当前时间}\le NotAfter NotBefore≤当前时间≤NotAfter
证书序列号用于签发者区分证书,并用于撤销列表和状态查询。证书没有过期,不代表它没有被撤销;私钥泄露、错误签发、域名控制权变化等情况都可能需要提前撤销。
撤销检查可能通过 CRL、OCSP、OCSP Stapling 或浏览器自有机制完成。不同客户端的策略不完全相同,不能把"当前没有查到撤销信息"简单等同于"证书绝对安全"。
四、证书链是怎样建立信任的
4.1 根证书是信任锚
浏览器和操作系统预先安装一批根 CA 证书。根证书通常是自签名的:
Verify R o o t P u b l i c ( Sign R o o t P r i v a t e ( T B S ) ) = true \operatorname{Verify}{RootPublic}(\operatorname{Sign}{RootPrivate}(TBS))=\text{true} VerifyRootPublic(SignRootPrivate(TBS))=true
但根证书自签名成功并不是它值得信任的原因。信任来自软件发行者、操作系统厂商、企业管理员或用户明确把它加入信任库。
4.2 中间 CA 和终端证书
典型证书链如下:
text
根 CA
↓ 签发
中间 CA
↓ 签发
网站终端证书
浏览器验证时要完成:
- 使用中间 CA 公钥验证网站证书;
- 使用根 CA 公钥验证中间 CA 证书;
- 检查中间 CA 的
CA=true和keyCertSign; - 检查路径长度和名称约束;
- 检查终端证书的 SAN、EKU、有效期和算法;
- 确认链最终落到本地信任锚。
证书链不是"只要找到一个能验签的上级就成功"。每一层都有用途、路径和策略约束。
4.3 为什么服务器通常发送中间证书
客户端通常已经内置根证书,但不一定内置某个网站所需的中间 CA。服务器因此通常发送:
- 自己的终端证书;
- 必要的中间 CA 证书;
- 一般不发送根证书。
如果服务器忘记配置中间链,一些客户端可能无法构建完整路径,从而报告证书链不完整。不同操作系统的缓存和信任库差异可能让问题表现不一致。
4.4 交叉签发与多条路径
同一个 CA 密钥可能存在不同签发路径。客户端可能根据本地信任库、算法策略和证书有效期选择不同链。于是同一网站在不同设备上可能出现不同验证结果。
排查证书链时,不能只看服务器发送的文本顺序,还要确认:
- 当前候选签发者是谁;
- 签名验证使用哪把公钥;
- 哪个根证书成为信任锚;
- 是否有过期或不再受信的交叉链;
- 本地信任库是否已经更新。
五、HTTPS 握手中证书、签名与 ECDHE 的分工
5.1 证书:绑定长期身份公钥
证书声明:某个 CA 认可某个身份与公钥之间的绑定:
I D S ⟷ P K S ID_S\longleftrightarrow PK_S IDS⟷PKS
客户端通过证书链、名称和用途检查,决定是否接受这个绑定。
5.2 ECDHE:建立本次会话的共享秘密
服务器和客户端生成临时密钥对:
Q C = x C G , Q S = x S G Q_C=x_CG,\qquad Q_S=x_SG QC=xCG,QS=xSG
双方计算:
Z = x C Q S = x S Q C Z=x_CQ_S=x_SQ_C Z=xCQS=xSQC
这个共享秘密用于派生本次会话密钥。它不是证书公钥,也不是长期身份密钥。
5.3 CertificateVerify:证明私钥持有
服务器使用证书对应的长期私钥,对当前握手上下文进行签名:
σ = Sign s k S ( context ∥ H ( transcript ) ) \sigma=\operatorname{Sign}_{sk_S}(\text{context}\|H(\text{transcript})) σ=SignskS(context∥H(transcript))
客户端用证书中的公钥验证。这样客户端不仅知道"某 CA 签发过一张证书",还知道当前连接对端确实持有对应私钥。
5.4 Finished:确认双方看到同一个握手
Finished 使用从握手秘密派生的验证密钥,认证当前完整 transcript:
V e r i f y D a t a = MAC F i n i s h e d K e y ( H ( transcript ) ) VerifyData=\operatorname{MAC}_{FinishedKey}(H(\text{transcript})) VerifyData=MACFinishedKey(H(transcript))
如果攻击者替换了版本、扩展、临时公钥或证书相关消息,Finished 通常无法验证通过。
六、中间人攻击是怎样发生的
6.1 裸 DH/ECDH 的中间人攻击
假设 Alice 想与 Bob 建立 ECDH。Alice 发送 Q A Q_A QA,Bob 发送 Q B Q_B QB。如果没有身份认证,Mallory 可以拦截并替换:
text
Alice Mallory Bob
|------ Q_A ---------->| |
|<---------------------|--------- Q_M ----------|
| | |
|<------ Q_M ----------| |
| |<--------- Q_B ---------|
结果是:
- Alice 与 Mallory 计算共享密钥 K A M K_{AM} KAM;
- Mallory 与 Bob 计算共享密钥 K M B K_{MB} KMB。
Mallory 可以解密 Alice 的请求,再用另一把密钥重新加密发给 Bob;Bob 的响应也可以被反向处理。这就是典型的中间人攻击。
6.2 HTTPS 如何阻止这种攻击
HTTPS 使用证书和握手签名把服务器身份绑定到当前握手:
- Mallory 发送自己的临时公钥;
- Alice 要求 Mallory 提供一个对
example.com有效的证书; - Mallory 如果没有受信 CA 签发的合法证书,证书链或 SAN 验证失败;
- 即使 Mallory 复制了真实服务器证书文件,也没有对应私钥,无法完成握手签名;
- 如果 Mallory 自己签发证书,浏览器默认不信任其根 CA。
因此攻击者必须突破信任链、获得目标私钥、控制客户端信任库,或诱导客户端关闭验证。
6.3 复制证书不等于复制身份
证书是公开材料,攻击者通常可以下载网站证书。真正需要保护的是与证书公钥对应的私钥:
Q = PublicKey ( d ) Q=\operatorname{PublicKey}(d) Q=PublicKey(d)
只有掌握 d d d,攻击者才能对握手 transcript 生成有效签名。证书文件泄露本身通常不是私钥泄露。
6.4 伪造域名证书
如果攻击者能够骗过 CA 的域名控制验证,就可能获得一个对目标域名有效的证书。客户端此时可能无法仅靠证书签名区分攻击者和真实站点。
这说明 PKI 的安全性不仅依赖数学,还依赖:
- CA 身份验证流程;
- 域名控制权保护;
- 证书透明度和监控;
- 证书撤销;
- 私钥保护;
- 浏览器和操作系统的信任策略。
七、企业 HTTPS 代理为什么能够解密流量
7.1 企业根证书模型
在企业内网、杀毒软件或调试代理中,常见 TLS 检查模式是:
text
客户端 ←→ 企业代理 ←→ 真实服务器
企业管理员把一个自有根 CA 证书安装到客户端信任库。代理访问真实服务器,再为目标域名动态生成一张由企业根 CA 签发的证书。客户端看到证书链最终连接到自己信任的企业根 CA,因此接受代理。
代理分别与两端建立 TLS 会话:
- 客户端与代理使用一把会话密钥;
- 代理与真实服务器使用另一把会话密钥。
代理能够读取和重新加密 HTTP 内容,因此这不是"突破了 TLS 数学",而是客户端明确把代理根证书纳入信任边界。
7.2 为什么普通恶意软件不能简单照搬
恶意软件若想采用相同方法,必须让客户端信任它的根证书,或者控制浏览器、操作系统、企业配置或应用自带信任库。现代系统会通过:
- 根证书安装权限;
- 企业策略;
- 证书固定或应用级信任;
- 安全启动和设备管理;
- 用户提示与审计
限制这种行为。但一旦恶意根 CA 被安装到受信任的信任库中,影响范围可能非常大。
7.3 证书固定的边界
证书固定(pinning)让应用额外期待某个公钥或证书集合,而不是完全依赖系统 CA。它可以降低误签发或企业代理的影响,但也会带来:
- 证书轮换困难;
- 密钥泄露后的更新问题;
- 备份公钥和迁移策略复杂;
- 错误固定导致线上服务不可用。
不能把固定机制当成所有场景的万能替代方案,应根据应用生命周期和更新能力设计。
八、如何排查 HTTPS 证书和中间人问题
8.1 先看当前连接的证书
授权环境中可以使用:
bash
openssl s_client -connect example.test:443 \\
-servername example.test -showcerts
重点查看:
Subject与Issuer;- SAN;
- 有效期;
- 公钥算法和曲线;
- KeyUsage、EKU;
- 服务器发送的中间证书;
- TLS 版本和密码套件。
命令输出只能帮助观察连接,不能替代应用实际的主机名和策略验证。
8.2 浏览器显示证书错误时
常见错误与含义:
- 证书过期或尚未生效:系统时间或证书生命周期问题;
- 名称不匹配:访问域名不在 SAN 允许范围;
- 证书链不受信:缺少中间证书、根不受信或企业证书未安装;
- 证书被撤销:证书不应继续使用;
- 弱算法或密钥:不符合客户端安全策略;
- 代理证书:当前连接可能经过企业 TLS 检查代理,也可能存在恶意中间人。
不要为了消除警告而直接点击"继续访问",应先确认连接目标、信任来源和代理环境。
8.3 观察证书是否被替换
可以在不同网络、不同设备和不同时间比较:
- 证书序列号;
- 公钥指纹;
- Issuer;
- 证书链;
- SAN 和有效期。
如果企业环境使用合法 TLS 检查,Issuer 通常会变成企业内部 CA;如果是恶意替换,可能出现陌生根 CA、异常有效期或不符合组织策略的签发者。
8.4 CTF 中的常见考点
- 证书链中给出伪造根或错误中间 CA;
- 客户端只验证证书签名,不验证 SAN;
- 客户端信任任意自签名证书;
- 证书过期但程序忽略时间;
- 证书与服务器私钥不匹配;
- 代理替换证书但客户端导入了代理根;
- 从证书或私钥文件中恢复弱 RSA 参数;
- TLS 版本或密码套件降级;
- 应用自带信任库与系统信任库不一致。
排查时应记录程序实际检查的字段,而不是只看浏览器是否显示一个绿色锁图标。
九、常见误区
9.1 HTTPS 可以隐藏 IP 和域名
HTTPS 主要保护应用层内容。IP、连接时间、流量大小以及部分握手信息仍可能暴露。需要额外的网络隐私方案才能减少元数据泄露。
9.2 有证书就一定是目标网站
证书必须进一步检查 SAN、链、有效期、用途和当前连接是否持有对应私钥。证书文件本身可以被任何人复制。
9.3 自签名证书一定不安全
自签名证书没有进入公共浏览器信任体系,但在受控内网、设备配对或显式分发信任指纹的场景中可以安全使用。关键是信任是否通过安全渠道建立,而不是证书是否由公共 CA 签发。
9.4 中间人只能发生在 HTTP 明文连接
即使使用 HTTPS,如果客户端关闭验证、信任恶意根证书、没有做主机名校验或应用协议存在降级路径,仍可能遭受中间人攻击。
9.5 证书固定可以替代所有 PKI
固定机制有适用边界,不能忽略密钥轮换、备份、更新和设备管理。错误固定同样可能造成大面积不可用。
十、总结
HTTPS 的安全主线可以写成:
URL/主机名 → 证书链验证 → 握手签名 → ECDHE → KDF → AEAD HTTP \text{URL/主机名}\rightarrow\text{证书链验证}\rightarrow\text{握手签名}\rightarrow\text{ECDHE}\rightarrow\text{KDF}\rightarrow\text{AEAD HTTP} URL/主机名→证书链验证→握手签名→ECDHE→KDF→AEAD HTTP
证书链解决"哪个公钥被哪个信任锚认可";SAN 解决"证书是否对应当前访问的主机名";CertificateVerify 解决"当前对端是否持有对应私钥";ECDHE 解决"本次会话如何建立临时共享秘密";Finished 和 AEAD 解决"握手和应用数据如何保持完整性与机密性"。
中间人攻击通常不是破解 AES 或椭圆曲线,而是利用身份认证缺失、信任库错误、主机名校验缺失、恶意根证书或私钥泄露。理解每个环节的职责,才能判断一次 HTTPS 连接到底保护到了哪里。