Skip to content

中间人攻击与 HSTS

基于 HTTP 现代标准 · 核于 2026-06

速查

  • 中间人攻击(MITM):攻击者潜伏在客户端与服务端之间,截获、读取、篡改、转发流量,对双方都「隐形」;现代术语更准确叫在途攻击者(on-path attacker)。
  • 经典落地场景:公共 Wi-Fi(伪造热点 / 被控路由器)、ARP 欺骗、DNS 投毒——只要攻击者能进到链路里,就能当「中间人」。
  • HTTPS 用两件武器抵御 MITM:加密(截到也读不懂)+ 证书验证(确认对端确是目标服务端,而非冒名者)。
  • 攻击者的突破口:诱导用户点掉证书告警、骗装恶意根证书(如企业代理 / 监控软件),或干脆不让你用上 HTTPS——即 SSL 剥离。
  • SSL 剥离(SSL Stripping):用户首次以 http:// 访问时,攻击者拦下请求、把本应跳转的 HTTPS 降级成明文 HTTP,自己对服务端走 HTTPS,对用户走 HTTP,全程读取明文。
  • HSTSStrict-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」:

  1. 伪造证书但无可信签名:攻击者可以自签一张 bank.com 的证书,但没有受信 CA 背书,浏览器会直接报错。除非用户被诱导点掉告警,或被骗在系统里装了攻击者的根证书(企业代理、某些「安全软件」、被植入的设备就是这么做合法或恶意中间人的)。
  2. 降级到明文:既然 HTTPS 这么难破,那就不让你用 HTTPS——把连接拉回 HTTP,加密和证书验证一起失效。这就是下面的 SSL 剥离。

证书为何可信、信任链如何建立属上游话题,详见 TLS 握手流程 与本章证书相关页;本页只关注「验证被绕过」时会发生什么。

SSL 剥离:把 HTTPS 偷偷降级成 HTTP

SSL 剥离(SSL Stripping)是最现实的 MITM 变体,它不去硬碰加密,而是攻击那道最脆弱的首跳:用户在地址栏敲 example.com、或点了一个 http:// 旧链接时,浏览器第一下往往是明文 HTTP,本应被服务端 301 跳转到 HTTPS。攻击者就卡在这一跳:

text
用户 ──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://——根本不给攻击者拦明文的机会。

http
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
# ① 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,根本不发明文请求。入选流程:

  1. 头里满足 max-age ≥ 31536000(1 年)、带 includeSubDomains、带 preload
  2. hstspreload.org 提交域名,审核通过后随浏览器更新内置。

preload 是「重承诺」

进了名单意味着该域名及其全部子域被硬编码为只能 HTTPS,且移出名单要等浏览器更新、周期很长。上 preload 前务必确认所有子域(含未来要建的)都已就绪 HTTPS,否则会把自己的子域「锁死」在无法访问的状态。

证书钉扎的现状:HPKP 已废弃

除了「只用 HTTPS」,业界还试过更激进的证书钉扎(certificate pinning):让站点声明「只认这几把特定公钥」,即便某个 CA 被攻陷、签发了伪造证书,浏览器也会因公钥对不上而拒绝——理论上能挡住「合法 CA 被滥用」式的 MITM。

  • HPKPPublic-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 握手流程;下一页转入把这些落到地的工程实践——证书实务