banner
约 2,100 字
7 分钟

HTTPS 中间人攻击:原理、实践、为什么 HTTPS 还不够

摘要

本文指出 HTTPS 并非绝对安全,中间人攻击(MITM)常因实现不当被绕过。文章分析了 MITM 的原理及常见绕过手段(如弱证书校验、用户忽略警告)。作者通过实验发现,浏览器和移动 App 在证书验证上存在漏洞,极易被劫持。防御需依赖强制 HTTPS、HSTS、证书锁定及 VPN 等措施。结论是开发者必须严格实现安全协议,用户不应盲目信任“安全”的 App。

第一次知道 HTTPS 时觉得——这下网络安全了。后来才知道,HTTPS 拦不住中间人攻击,拦得住的只有"正确实现的 HTTPS"。此文是 3 篇相关文章读下来的笔记 + 我自己的验证

什么是中间人攻击

MITM(Man-in-the-Middle):攻击者把自己塞到客户端和服务端中间,两边的通信都要经过他

两种玩法:

  • 被动:只监听,不改内容

  • 主动:拦截 + 篡改,改完再转发

危害:账号密码、cookie、支付信息、聊天记录——全部裸奔

HTTPS 怎么挡 MITM

HTTPS = HTTP + TLS。TLS 干三件事:

  • 加密:传输内容用对称加密(AES),第三方看不懂

  • 认证:服务端发 X.509 证书证明自己是谁

  • 完整性:用 MAC/HMAC 校验内容没被改

关键就在证书:客户端收到服务端证书时,会验证:

  1. 是不是受信任的 CA签发的

  2. 有没有过期

  3. 签发的域名和访问的域名是否一致

  4. 有没有被 CA 吊销(CRL/OCSP)

这 4 条全过,才认为对面是真的。任何一条不过,浏览器就报警。

但是:HTTPS 真的能挡吗?

问得好。理论上能挡,实践上经常被绕过

绕过 1:弱证书校验

很多 App 写代码时偷懒,不验证证书,或者验证逻辑有 bug:

  • 不检查证书是否过期

  • 不检查证书域名

  • 遇到自签名证书直接信任

  • 实现自定义 TrustManager,把所有证书都当合法的

这种 App 用 HTTPS,等于没用。攻击者随便伪造一个证书就能劫持。

绕过 2:CA 被骗签发证书

历史上多次发生 CA 误签或被黑导致假证书流入市场(Symantec、 DigiNotar 都出过事)。攻击者拿到合法证书,浏览器看不出来

绕过 3:用户主动忽略警告

浏览器弹"证书错误"对话框,用户点"继续访问"。这等于把整个 HTTPS 体系作废。

实战:在自己机器上复现一次 MITM

纸上得来终觉浅。我搭了一次完整的 MITM 环境,看看 HTTPS 在实践中到底有多脆弱。

准备工作:

  • 一个域名(DV 证书网上能免费申请,Let's Encrypt 一把梭)

  • 一台 nginx 服务器,配置该域名的 HTTPS

  • Fiddler + FreeHttp 插件,用来重定向流量

  • 一台手机,被攻击对象

步骤 1:搭 nginx 中间人

nginx
# /etc/nginx/conf.d/mitm.conf
server {
    listen 443 ssl;
    server_name lulianqi.com;

    ssl_certificate     /etc/nginx/ssl/lulianqi.com.crt;
    ssl_certificate_key /etc/nginx/ssl/lulianqi.com.key;

    location / {
        return 200 "中间人劫持成功!\n";
        add_header Content-Type text/plain;
    }
}

这个 nginx 实际上就是中间人。当流量被引导到这里时,所有 HTTPS 都会和这台服务器建立 SSL 通道,而不是真正的目标。

步骤 2:用 Fiddler 重定向 CONNECT 目标

关键点:CONNECT 请求是明文的,在 SSL 通道建立之前。这时候改请求目标是可行的

Fiddler + FreeHttp 插件,加规则:

纯文本
匹配: CONNECT www.baidu.com:443
替换为: CONNECT lulianqi.com:443

还要关掉 "skip connect tunnels",让 Fiddler 把 CONNECT 转发到我们自己的服务器。

步骤 3:观察浏览器反应

用浏览器访问 https://www.baidu.com——

Chrome:直接报错,不能继续。因为 baidu.com 开了 HSTS,浏览器记录过"必须 HTTPS",证书不对直接禁止。

Edge:弹一个"证书错误"的对话框,询问是否继续。点继续就能访问——但用户已经被劫持了

这就是为什么我前面说"HTTPS 拦不住用户主动忽略警告"。

移动 App:情况更糟

浏览器还算有底线,App 很多都没有

同一个 MITM 环境,把手机连上 Fiddler 代理(不需要装证书),试着打开各种 App:

知乎 iOS

直接无视证书错误,和正常访问一样。所有浏览、发帖、登录的流量都被中间人解密。用户毫无察觉

360 浏览器 iOS

更离谱——号称"安全"的浏览器,遇到证书错误只把绿色小盾牌变成灰色,没有任何询问,直接建立 SSL 通道。所有浏览内容裸奔

其他

部分金融 App、部分 App 的部分模块,都存在类似问题。开发者忽视了证书校验,或者对错误处理不当。

教训:App 是不是真安全,光看代码看不出来的要拿 MITM 工具实际验证

MITM 的攻击向量

上面实验的"重定向 CONNECT"在真实攻击里很少见。更常见的 MITM 攻击路径

  • ARP 欺骗:内网里伪造网关 MAC,所有流量先过我

  • DNS 劫持:把目标域名解析到攻击者 IP,改 hosts 文件就能模拟

  • WPAD 劫持:自动代理配置被篡改

  • BGP 路由劫持:网络层劫持,国家级手段

  • 恶意 Wi-Fi:公共热点直接 ARP 欺骗

这些都不需要用户连接任何代理,链路上的网络设备就能做。所以"别连不认识的 Wi-Fi"不是开玩笑。

为什么 HTTPS 不够 + 还要这些

有人会问:我用 RSA 加密所有请求不就行了,为什么一定要 HTTPS?

答案是:HTTPS = 加密 + 身份认证 + 完整性三个一起。只用 RSA 加密:

  • 密钥交换麻烦:每对通信双方都要换密钥,管理噩梦

  • 没有身份认证:客户端不知道对面是谁,攻击者替换 RSA 公钥就破功

  • 性能差:RSA 比 AES 慢两个数量级,不适合大数据

  • 无标准:每个团队一套实现,互相不通

HTTPS 的优雅之处:握手时用 RSA/ECDHE 交换密钥之后用 AES 加密数据两边的好处都占了

怎么真拦住 MITM

几个层次:

1. 强制 HTTPS + HSTS

服务端返回 Strict-Transport-Security: max-age=31536000告诉浏览器"以后必须 HTTPS"。浏览器记下,下次证书不对直接拒绝,不给用户"继续访问"的选项。

2. Certificate Pinning

App 里硬编码目标服务端的证书指纹(或公钥),只信任这个CA 都被骗了也用不了

代价:证书更新要发版,运维麻烦。所以一般只对核心 API 做 pinning。

3. DNS 层

  • DNSSEC:DNS 响应签名,防 DNS 劫持

  • DoH / DoT:DNS 走加密通道,中间人看不见

4. 网络层

  • VPN:所有流量走加密隧道,公网 Wi-Fi 上必备

  • 不连陌生 Wi-Fi:老生常谈,但真有用

5. 客户端正确实现

这一点最重要但最容易被忽略:

  • 用系统 TLS 库不要自己写

  • 不要禁用证书验证

  • 不要信任自签名证书(除非是预置的

  • 遇到错误不要静默"重试"或"忽略"

我自己的部署习惯

服务端:

  • 所有站点强制 HTTPS(nginx 301)

  • 开 HSTS,max-age 一年

  • 证书用 Let's Encrypt 自动续期

  • 禁用过时的 TLS 版本(只开 TLS 1.2 / 1.3

客户端:

  • App 不写自定义 TrustManager,用系统默认

  • 关键 API 加 certificate pinning

  • 出错就报错,不静默重试

网络:

  • 公共 Wi-Fi 上一定开 VPN

  • 家里 DNS 走 DoH

  • 路由器强制管理页面 HTTPS

写在最后

MITM 攻击的可怕之处不在技术,在用户的"无感"

你看到 HTTPS 标志、看到绿色小盾牌、看到 App 正常显示内容——但这一切都可能是中间人在演戏安全的责任不在用户,在开发者

读完那 3 篇文章最深的感受:别相信任何说自己"安全"的 App,除非你亲手用 MITM 工具验证过证书校验是真生效的

这个时代,"实现了 HTTPS" 离 "HTTPS 真起作用" 还有 100 公里

END