10、不安全的通信:HTTPS降级攻击、证书校验绕过、SSL Pinning绕过

说实话,移动端的安全通信,一直是个「看起来简单,做起来全是坑」的领域。很多开发者觉得,只要用了 HTTPS,数据就安全了。嗯,这个想法太天真了。

我这些年做安全测试,遇到过太多「明明上了 HTTPS,却被中间人打得满地找牙」的案例。今天我们就来聊聊,攻击者是怎么绕过 HTTPS 保护的,以及我们该怎么防。

核心观点:HTTPS 不等于安全。真正的安全,取决于你如何实现它。

10.1 HTTPS 降级攻击

什么是降级攻击?说白了,就是攻击者强迫客户端和服务器使用较弱的加密协议,然后利用这个弱点进行破解。

我记得有一次,我在测试一个金融类 App。它的服务器明明支持 TLS 1.2,但客户端却主动降级到了 TLS 1.0。为什么?因为开发者在代码里写死了最低版本。结果呢?攻击者只要在中间拦截一下,就能让双方用上 TLS 1.0,然后利用 BEAST 攻击解密数据。

常见的降级攻击手法

  • 协议降级:强制使用 TLS 1.0 或 SSL 3.0
  • 密码套件降级:强制使用弱加密算法(如 RC4、CBC 模式)
  • 版本回退:利用 TLS_FALLBACK_SCSV 机制缺陷

如何检测降级攻击

我个人习惯用 nmap 配合 ssl-enum-ciphers 脚本,先扫一遍服务器支持的协议和密码套件。然后抓包看客户端实际协商的结果。

# 扫描服务器支持的 TLS 版本和密码套件
nmap --script ssl-enum-ciphers -p 443 target.com

# 抓包查看协商过程
tcpdump -i any -s 0 -w handshake.pcap port 443

如果发现客户端支持 TLS 1.2,但实际协商出来的是 TLS 1.0,那基本可以断定有问题了。

避坑指南:我曾经见过一个 App,在 Android 4.4 上跑得好好的,到了 Android 7.0 上反而降级了。原因是开发者用了过时的 SSLSocketFactory,没有适配新的安全策略。所以,记得检查不同系统版本下的行为。

10.2 证书校验绕过

证书校验绕过,是移动端最常见的安全漏洞之一。为什么?因为很多开发者觉得「自签名证书太麻烦」,或者「测试环境懒得配」,就直接把校验给关了。

你想想看,一个 App 如果连服务器证书都不验证,那中间人攻击简直就像逛自家后院一样简单。攻击者只要在同一个 Wi-Fi 下,用 mitmproxy 或者 Burp Suite 就能轻松劫持所有流量。

常见的绕过方式

  1. 信任所有证书:重写 checkServerTrusted() 方法,直接返回
  2. 空 TrustManager:创建一个不验证任何证书的 TrustManager
  3. 忽略域名校验:不检查证书中的 CN 或 SAN 字段
  4. 使用 HostnameVerifier 返回 true:直接允许所有主机名

代码示例:典型的错误实现

// 错误示例:信任所有证书
TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain, String authType) {}
        public void checkServerTrusted(X509Certificate[] chain, String authType) {}
        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
    }
};

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

// 错误示例:忽略域名校验
HttpsURLConnection.setDefaultHostnameVerifier(
    (hostname, session) -> true
);

这段代码,我敢说在 GitHub 上随便搜一下,能搜出一堆。很多开发者觉得「先这样写着,上线前再改」,结果上线前忘了,就带着漏洞上线了。

我的建议:测试环境和生产环境用不同的证书,但校验逻辑必须一致。不要为了测试方便而关闭校验。你可以用 Network Security Config(Android 7.0+)来管理调试证书,而不是直接改代码。

10.3 SSL Pinning 绕过

SSL Pinning,也叫证书绑定。它的原理很简单:客户端只信任特定的证书或公钥,而不是系统信任的所有 CA。这样即使攻击者拿到了 CA 签发的假证书,也无法通过校验。

但是,SSL Pinning 也不是万能的。我遇到过不少 App,虽然做了 Pinning,但实现方式有问题,照样能被绕过。

常见的绕过手法

手法 原理 工具
Frida Hook Hook 证书校验方法,返回固定值 Frida + objection
Xposed 模块 替换 TrustManager 实现 JustTrustMe
重打包 修改 APK 中的证书或校验逻辑 apktool + jadx
内核级绕过 替换系统 CA 证书存储 Magisk + MoveCert

Frida 绕过示例

// Frida 脚本:绕过 SSL Pinning
Java.perform(function() {
    var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
    TrustManagerImpl.checkServerTrusted.implementation = function(chain, authType) {
        // 直接返回,不校验
        return;
    };

    var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier');
    HostnameVerifier.verify.implementation = function(hostname, session) {
        return true;
    };
});

用 objection 更简单,一条命令搞定:

objection -g com.target.app explore
android sslpinning disable

嗯,这里要注意。objection 的 sslpinning disable 并不是万能的。它只能绕过一些常见的实现方式。如果 App 用了 Native 层的 Pinning,或者用了 OkHttp 的 CertificatePinner,那就需要更复杂的操作了。

关键点:SSL Pinning 的强度取决于它的实现层级。Java 层的 Pinning 最好绕,Native 层次之,双校验(Java + Native)最难。

10.4 知识体系总览

下面这张图,是我自己总结的「不安全通信攻击与防御」知识体系。你可以把它当作一个检查清单,做安全测试时对照着看。

不安全的通信:攻击与防御 HTTPS降级攻击 证书校验绕过 SSL Pinning绕过 协议降级 密码套件降级 版本回退攻击 信任所有证书 空TrustManager 忽略域名校验 Frida Hook Xposed模块 重打包/内核级 强制TLS 1.2+ 禁用弱密码套件 启用TLS_FALLBACK_SCSV 实现完整校验逻辑 使用Network Security Config 测试环境独立证书 多层Pinning Native层校验 定期更新证书指纹 防御强度 = 实现层级 × 校验完整性

10.5 实战建议

说了这么多攻击手法,最后给几条实在的建议:

  • 别偷懒:证书校验和 Pinning 一定要做,而且要做对。不要为了测试方便留下后门。
  • 多层防御:Java 层做一层,Native 层再做一层。攻击者绕了一层还有一层。
  • 定期更新:证书指纹会过期,记得在 App 更新时同步更新 Pinning 的指纹。
  • 监控异常:如果发现大量证书校验失败,可能是中间人攻击,要及时告警。

最后说一句:安全测试不是一次性的事。每次版本更新,都要重新检查通信安全。我见过太多「上次测了没问题,这次改了个小功能就出漏洞」的案例了。


公众号:蓝海资料掘金营,微信deep3321