HTTPS抓包进阶:SSL Pinning绕过原理、证书双向验证破解、Android 7.0+与iOS 14+系统级证书信任机制
说实话,很多搞了几年安全测试的朋友,一遇到HTTPS抓包就头疼。特别是现在App越来越精,你装个代理它就罢工,你挂个证书它就报错。嗯,这背后其实就是SSL Pinning和证书验证在作怪。今天咱们就把这几个硬骨头啃下来。
核心认知:HTTPS抓包的难点从来不在协议本身,而在客户端对证书的校验逻辑。绕过校验,你就拿到了明文。
2.1 SSL Pinning——App的“证书指纹锁”
SSL Pinning,说白了就是App在代码里写死了它信任的证书或公钥。你抓包工具自己签发的证书,App根本不认。我在项目中遇到过好几次,明明Charles/Fiddler都配好了,App就是死活不走代理,一查日志,全是证书校验失败。
2.1.1 两种常见的Pinning方式
| 类型 | 绑定对象 | 绕过难度 | 典型场景 |
|---|---|---|---|
| Certificate Pinning | 完整的服务端证书 | 中等 | 证书不常变动的金融类App |
| Public Key Pinning | 证书中的公钥(或公钥哈希) | 较高 | 证书轮换频繁的大型互联网App |
为什么公钥Pinning更难?因为证书可以换,但公钥对(RSA或ECC)通常几年才换一次。你想想看,就算你伪造了证书,公钥对不上,App照样拒绝连接。
2.1.2 绕过Pinning的常规思路
绕过Pinning,本质上就两条路:要么Hook掉校验函数,要么把抓包工具的证书塞进App信任的根证书列表里。我个人习惯先用Hook方案,因为快,不用改App包体。
- Hook方案(推荐):使用Frida或Xposed,Hook掉SSLContext.init()、TrustManager.checkServerTrusted()等方法,让校验永远返回成功。
- 重打包方案:反编译App,在res/xml/network_security_config.xml里添加用户证书信任,或者直接替换App内置的Pinning公钥。
- JustTrustMe/SSLUnpinning:现成的Xposed模块,一键绕过常见Pinning库(OkHttp、HttpClient等)。
我的经验:遇到OkHttp 3.x以上版本,直接Hook CertificatePinner.check()最稳。Frida脚本大概10行代码就能搞定,比折腾重打包快得多。
2.2 证书双向验证——服务器也要查你的“身份证”
单向验证是客户端验服务端,双向验证是服务端也要验客户端。说白了,服务器会要求你出示一张它认可的客户端证书,拿不出来就断开连接。我在做某银行App测试时就遇到过,代理配好之后,连接直接RST,抓包工具连Hello都发不出去。
2.2.1 双向验证的握手流程
- 客户端发起ClientHello
- 服务端返回ServerHello + 服务端证书 + CertificateRequest(要求客户端证书)
- 客户端发送ClientCertificate + ClientCertificateVerify
- 服务端验证客户端证书的签发者、有效期、是否吊销
- 验证通过,继续密钥协商;验证失败,直接Alert并断开
为什么会这样?因为服务端在握手阶段就要求客户端证书,你抓包工具作为中间人,拿不出合法的客户端证书,自然就被拒了。
2.2.2 破解双向验证的两种方法
| 方法 | 适用场景 | 操作要点 |
|---|---|---|
| 提取客户端证书 | App内嵌了.p12或.bks证书文件 | 反编译App,从assets或raw目录提取证书文件,导入抓包工具 |
| Hook证书加载逻辑 | 证书通过代码动态加载或由服务器下发 | Hook KeyManagerFactory.init(),替换或导出证书 |
注意:提取到的客户端证书通常有密码保护。密码可能在App代码里硬编码,也可能通过算法生成。我曾经遇到过密码是设备IMEI+时间戳拼接后MD5的情况,折腾了一下午才跑出来。
2.3 Android 7.0+与iOS 14+的系统级证书信任机制
这两个版本是个分水岭。Android 7.0开始,系统默认不再信任用户安装的CA证书;iOS 14开始,即使安装了描述文件,系统也不自动信任。很多朋友在这上面栽过跟头——证书装了,代理配了,App就是不走。
2.3.1 Android 7.0+:Network Security Configuration
Android从7.0(API 24)引入了网络安全配置。默认情况下,只有系统预装证书才被信任。用户安装的Charles/Fiddler证书,对targetSdkVersion >= 24的App来说,等于废纸。
- debug模式:在AndroidManifest.xml的application标签加
android:networkSecurityConfig="@xml/network_security_config",然后在xml文件里配置信任用户证书。 - release模式:要么重打包修改network_security_config.xml,要么用Frida绕过。我个人习惯用Frida,因为不用改包,省去签名校验的麻烦。
避坑指南:我曾经在重打包时忘了处理签名校验,结果App启动就闪退。后来学乖了,先用Frida跑一下,确认是证书问题还是签名问题,再决定要不要重打包。
2.3.2 iOS 14+:证书信任开关
iOS 14之后,安装描述文件只是第一步。你还需要去“设置 → 通用 → 关于本机 → 证书信任设置”里,手动打开抓包工具证书的开关。这一步很多人会漏掉。
而且iOS对证书的校验更严格:
- 证书有效期必须合法
- 证书链必须完整
- 部分App会额外校验证书的SHA-256指纹
嗯,这里要注意:iOS上如果App使用了NSURLSession并设置了serverTrust,即使系统信任了你的证书,App照样可以拒绝。这时候还是得上Frida或SSLKillSwitch2。
2.4 知识体系总览
下面这张图把HTTPS抓包进阶的核心逻辑串起来了。你可以把它当作排查问题的路线图:遇到抓包失败,先定位是哪个环节出了问题。
2.5 实战建议与避坑清单
最后,我把自己这些年踩过的坑整理成了一份清单。你遇到问题的时候,可以对照着排查。
- 先确认App是否使用了Pinning:挂上代理,看报错是“证书不信任”还是“连接被拒绝”。前者是系统级问题,后者多半是Pinning。
- Frida脚本不要贪多:一次只Hook一个目标函数,Hook多了容易冲突。我遇到过同时Hook了checkServerTrusted和CertificatePinner.check,结果App直接崩溃。
- iOS证书信任开关别忘了:装了描述文件不等于信任了。去设置里手动打开,这一步漏掉的人最多。
- 双向验证优先找客户端证书:反编译App,搜.p12、.bks、.pfx后缀,或者搜KeyStore、PKCS12等关键词。找到了就成功了一半。
- Android 7.0+重打包后要处理签名:用apktool重打包后,记得用uber-apk-signer重新签名。否则App安装不上,或者运行时被检测到签名变更。
重要提醒:绕过SSL Pinning和证书验证,仅限用于合法授权的安全测试、漏洞挖掘或自己的设备调试。未经授权对他人App进行抓包和破解,可能违反计算机信息系统安全相关法律法规。