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 就能轻松劫持所有流量。
常见的绕过方式
- 信任所有证书:重写
checkServerTrusted()方法,直接返回 - 空 TrustManager:创建一个不验证任何证书的 TrustManager
- 忽略域名校验:不检查证书中的 CN 或 SAN 字段
- 使用 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 知识体系总览
下面这张图,是我自己总结的「不安全通信攻击与防御」知识体系。你可以把它当作一个检查清单,做安全测试时对照着看。
10.5 实战建议
说了这么多攻击手法,最后给几条实在的建议:
- 别偷懒:证书校验和 Pinning 一定要做,而且要做对。不要为了测试方便留下后门。
- 多层防御:Java 层做一层,Native 层再做一层。攻击者绕了一层还有一层。
- 定期更新:证书指纹会过期,记得在 App 更新时同步更新 Pinning 的指纹。
- 监控异常:如果发现大量证书校验失败,可能是中间人攻击,要及时告警。
最后说一句:安全测试不是一次性的事。每次版本更新,都要重新检查通信安全。我见过太多「上次测了没问题,这次改了个小功能就出漏洞」的案例了。
公众号:蓝海资料掘金营,微信deep3321