中间人攻击与 HSTS
基于 HTTP 现代标准 · 核于 2026-06
速查
- 中间人攻击(MITM):攻击者潜伏在客户端与服务端之间,截获、读取、篡改、转发流量,对双方都「隐形」;现代术语更准确叫在途攻击者(on-path attacker)。
- 经典落地场景:公共 Wi-Fi(伪造热点 / 被控路由器)、ARP 欺骗、DNS 投毒——只要攻击者能进到链路里,就能当「中间人」。
- HTTPS 用两件武器抵御 MITM:加密(截到也读不懂)+ 证书验证(确认对端确是目标服务端,而非冒名者)。
- 攻击者的突破口:诱导用户点掉证书告警、骗装恶意根证书(如企业代理 / 监控软件),或干脆不让你用上 HTTPS——即 SSL 剥离。
- SSL 剥离(SSL Stripping):用户首次以
http://访问时,攻击者拦下请求、把本应跳转的 HTTPS 降级成明文 HTTP,自己对服务端走 HTTPS,对用户走 HTTP,全程读取明文。 - HSTS(
Strict-Transport-Security响应头):服务端声明「本站今后只准用 HTTPS」,浏览器记下后自动把http://升级为https://,从根上堵死降级。 - 三个指令:
max-age=<秒>(记忆时长)/includeSubDomains(覆盖全部子域)/preload(申请进浏览器预加载名单)。 - HSTS 期内浏览器不允许用户点掉证书错误——证书无效直接拦死,无「继续前往」选项。
- 首次访问空窗:HSTS 靠「先收到过头部」生效,从没访问过的站点仍可能被首跳降级。
- HSTS preload list:把域名写进浏览器内置名单,首次访问即强制 HTTPS,补上首访空窗;入选须
max-age ≥ 31536000(1 年)+includeSubDomains+preload,再到 hstspreload.org 提交。 - HSTS 头只在 HTTPS 上才被采信,HTTP 响应里的该头会被浏览器忽略;也无法经 HTTP 关闭(防中间人篡改)。
- 证书钉扎现状:HPKP(
Public-Key-Pins)因「自锁 / 勒索」风险已废弃并从浏览器移除;其继任尝试Expect-CT也已废弃——证书透明度(CT)如今由浏览器默认强制,无需再发头。
中间人攻击:潜伏在链路中间的「邮差」
中间人攻击(Man-in-the-Middle,MITM)指攻击者把自己插进通信双方之间,像一个会拆信的邮差:截下你寄出的每封信,拆开读、必要时改两笔,再重新封好寄给本来的收件人——而你和对方都浑然不觉。因为「中间人」一词带方位歧义(攻击者不必真在地理中间),现代文档更倾向用在途攻击者(on-path attacker)这一更精确的叫法。
要当上中间人,攻击者得先进到链路里,常见手法:
- 公共 Wi-Fi:架一个同名伪造热点(Evil Twin),或控制一台咖啡馆 / 机场的路由器,所有连上来的流量都过它的手。
- ARP 欺骗:在局域网内冒充网关,把同网段的流量引到自己机器。
- DNS 投毒:篡改域名解析结果,把
bank.com指向攻击者控制的 IP。
不要忽视证书告警
浏览器弹出「证书无效 / 不受信任」时,很可能正撞上一次 MITM 或钓鱼——你连的也许是冒名服务器。别点「继续前往」,明文链路 + 被无视的告警是攻击成功的两大前提。
HTTPS 如何抵御:加密 + 证书验证
HTTPS 对 MITM 的防御是两层叠加的,缺一不可:
- 加密:握手后流量全程加密,中间人即便截到字节流也是密文,读不懂、也难以悄悄篡改(完整性校验会暴露改动)。
- 证书验证:握手时服务端出示由受信任 CA 签发的证书,浏览器校验域名匹配、签名有效、未过期、未吊销,确认对端确实是目标服务端——这一步专门用来挡「冒名」。
于是攻击者想做中间人就只剩几个突破口,全都绕开了「正常的 HTTPS」:
- 伪造证书但无可信签名:攻击者可以自签一张
bank.com的证书,但没有受信 CA 背书,浏览器会直接报错。除非用户被诱导点掉告警,或被骗在系统里装了攻击者的根证书(企业代理、某些「安全软件」、被植入的设备就是这么做合法或恶意中间人的)。 - 降级到明文:既然 HTTPS 这么难破,那就不让你用 HTTPS——把连接拉回 HTTP,加密和证书验证一起失效。这就是下面的 SSL 剥离。
证书为何可信、信任链如何建立属上游话题,详见 TLS 握手流程 与本章证书相关页;本页只关注「验证被绕过」时会发生什么。
SSL 剥离:把 HTTPS 偷偷降级成 HTTP
SSL 剥离(SSL Stripping)是最现实的 MITM 变体,它不去硬碰加密,而是攻击那道最脆弱的首跳:用户在地址栏敲 example.com、或点了一个 http:// 旧链接时,浏览器第一下往往是明文 HTTP,本应被服务端 301 跳转到 HTTPS。攻击者就卡在这一跳:
用户 ──HTTP(明文,可被读改)──▶ 攻击者 ──HTTPS(加密)──▶ 服务端
◀──HTTP(攻击者剥掉了 https)── ◀──HTTPS──- 对用户这侧,攻击者全程维持明文 HTTP,把页面里的
https://链接、跳转统统改写成http://,用户浏览器始终没机会升级到 HTTPS——地址栏没有锁标,但很多人不会注意。 - 对服务端这侧,攻击者自己用正常 HTTPS 通信,服务端以为一切正常。
- 结果:用户的密码、Cookie、表单全走明文,被攻击者尽收眼底。这属于降级攻击(downgrade attack)的一种。
根因
SSL 剥离能成立,是因为「首次明文请求」给了攻击者拦截窗口。只要还有一次请求走 HTTP,就有被剥离的可能——HSTS 正是为消灭这个窗口而生。
HSTS:强制浏览器只用 HTTPS
HSTS(HTTP Strict Transport Security)让服务端通过一个响应头,命令浏览器「今后访问本站一律用 HTTPS」。浏览器记住这条策略后,任何 http:// 请求在发出前就被本地改写为 https://——根本不给攻击者拦明文的机会。
Strict-Transport-Security: max-age=31536000; includeSubDomains
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload三个指令:
| 指令 | 含义 | 要点 |
|---|---|---|
max-age=<秒> | 浏览器把「只用 HTTPS」记住多久 | 必填;每收到一次头就把过期时间从当下顺延,常设 1 年(31536000)或 2 年 |
includeSubDomains | 策略覆盖全部子域 | 可选;只对子域生效、不反向作用于父域,子域最好各自再发头 |
preload | 申请进浏览器预加载名单 | 可选;须同时满足 max-age ≥ 1 年 + includeSubDomains |
正确部署的姿势是:HTTP 入口只做永久跳转、不带 HSTS 头;跳转后的 HTTPS 响应里才发 HSTS 头:
# ① http://example.com 的响应:只跳转,不发 HSTS
HTTP/1.1 301 Moved Permanently
Location: https://example.com/
# ② https://example.com 的响应:在加密链路上才发 HSTS
HTTP/1.1 200 OK
Strict-Transport-Security: max-age=31536000; includeSubDomains三条铁律
- HSTS 头仅在 HTTPS 响应中被采信,HTTP 响应里的会被浏览器无视(否则中间人可伪造它捣乱)。
- HSTS 期内,证书出错无法被用户点掉——没有「我了解风险,继续前往」,直接拦死。
- HSTS 不能经 HTTP 关闭;要解除须在 HTTPS 上发
max-age=0,待下次安全请求才生效。
首访空窗与 preload list
HSTS 有个先天缺口:它靠「先收到过这个头」才生效,所以第一次访问(或记忆过期后)那一跳仍可能是明文 HTTP,照样能被 SSL 剥离。
HSTS preload list 补上了这个空窗:它是各大浏览器(Chrome、Firefox、Edge、Safari 等)内置在代码里的强制 HTTPS 名单,凡在册的域名,浏览器首次访问就直接用 HTTPS,根本不发明文请求。入选流程:
- 头里满足
max-age ≥ 31536000(1 年)、带includeSubDomains、带preload; - 到 hstspreload.org 提交域名,审核通过后随浏览器更新内置。
preload 是「重承诺」
进了名单意味着该域名及其全部子域被硬编码为只能 HTTPS,且移出名单要等浏览器更新、周期很长。上 preload 前务必确认所有子域(含未来要建的)都已就绪 HTTPS,否则会把自己的子域「锁死」在无法访问的状态。
证书钉扎的现状:HPKP 已废弃
除了「只用 HTTPS」,业界还试过更激进的证书钉扎(certificate pinning):让站点声明「只认这几把特定公钥」,即便某个 CA 被攻陷、签发了伪造证书,浏览器也会因公钥对不上而拒绝——理论上能挡住「合法 CA 被滥用」式的 MITM。
- HPKP(
Public-Key-Pins头)是它的标准化尝试,但已废弃并从浏览器移除。原因是风险远大于收益:配置一旦写错(钉错公钥、忘了备份钉),用户会被自己锁在门外、长时间无法访问,俗称 HPKP 自锁(HPKP suicide);它还衍生出勒索钉扎(攻击者拿到一次写头机会就钉死恶意公钥,把站点锁给攻击者)。MDN 的Public-Key-Pins页面如今已下线,可见其彻底退场。 - Expect-CT 头曾被用来过渡式地强制证书透明度(CT,要求证书登记到公开日志、便于发现误签),但它也已废弃(Chromium 107+ 起标记弃用,自 2021 年 6 月起基本失去意义)。原因是 CT 已由浏览器默认强制——新签发证书须自带 SCT(签名证书时间戳),不合规直接不被信任,无需再靠发头去「要求」。
今天该怎么做
普通站点不要再碰证书钉扎(HPKP)。抵御 MITM 与降级,现代组合是:全站 HTTPS + HSTS(条件具备再上 preload),再依赖浏览器默认的证书校验与证书透明度即可。极少数对供应链安全要求极高的客户端(如移动 App)才在应用层自行做钉扎,且需配套完善的轮换与回退机制。
小结
中间人 / 在途攻击者的核心是「插进链路、当隐形邮差」,HTTPS 用加密 + 证书验证双层防御让其无从冒名;攻击者于是转向SSL 剥离——攻击最脆弱的首次明文跳转、把 HTTPS 降级成 HTTP。HSTS 通过 Strict-Transport-Security 头命令浏览器只用 HTTPS、堵死降级窗口,includeSubDomains 扩到子域、preload 借浏览器内置名单连首访都强制;而 HPKP 证书钉扎因自锁与勒索风险已废弃、Expect-CT 也因 CT 默认强制而退场。理解了攻防全景,上一页可回看握手如何建立可信加密通道——TLS 握手流程;下一页转入把这些落到地的工程实践——证书实务。