简介
众所周知,我们在做手机端开发的时候,网络访问如果不做加密的话,请求数据很容易被抓包工具获取,从而造成安全隐患。所以我们怎么避免数据被别人通过抓包工具抓取呢?
SSL Pinning不是一个新概念,尽管很多开发者使用SSL来保证数据传输的安全性,但基本上都是未设置受信任证书,所以无法防止中间人攻击,数据还是可以被抓包。
证书锁定(SSL/TLS Pinning)顾名思义,将服务器提供的SSL/TLS证书内置到移动端开发的APP客户端中,当客户端发起请求时,通过比对内置的证书和服务器端证书的内容,以确定这个连接的合法性。
方法一
获取服务器证书摘要
创建一个脚本文件,certs.sh,文件内容如下:1
2
3
4
5
6
7
8
9
10
11
12#!/bin/bash
certs=`openssl s_client -servername $1 -host $1 -port 443 -showcerts </dev/null 2>/dev/null | sed -n '/Certificate chain/,/Server certificate/p'`
rest=$certs
while [[ "$rest" =~ '-----BEGIN CERTIFICATE-----' ]]
do
cert="${rest%%-----END CERTIFICATE-----*}-----END CERTIFICATE-----"
rest=${rest#*-----END CERTIFICATE-----}
echo `echo "$cert" | grep 's:' | sed 's/.*s:\(.*\)/\1/'`
echo "$cert" | openssl x509 -pubkey -noout |
openssl rsa -pubin -outform der 2>/dev/null |
openssl dgst -sha256 -binary | openssl enc -base64
done
使用方法如下,可以看到获取的证书摘要信息:1
2
3
4
5
6# ./certs.sh www.jllydj.gov.cn
/CN=www.jllydj.gov.cn
2o2MOiraYxPymc3kSEYyLx8IbdlToM0D5Je9PK/lSlk=
/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=Encryption Everywhere DV TLS CA - G1
GI75anSEdkuHj05mreE0Sd9jE6dVqUIzzXRHHlZBVbI=
通过okhttp设置CertificatePinner
1 | CertificatePinner certPinner = new CertificatePinner.Builder() |
方法二
Android okHttp 如何实现SSL Pinning
加载可信任的Ca证书
1
2
3
4CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
InputStream caInput = getResources().openRawResource(R.raw.ca);
Certificate ca = certificateFactory.generateCertificate(caInput);通过Ca创建一个包含可信任证书的keystore文件
1
2
3
4String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);Create a custom TrustManager from the trusted CAs in the keystore
1
2
3String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
trustManagerFactory.init(keyStore);设置到okhttp中
1
2
3
4
5
6
7
8
9
10
11
12// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagerFactory.getTrustManagers()[0]);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
这样就完成了android客户端的SSL PINning设置。
问题
方法一存在的问题
服务器SSL证书到期前,测试环境更换证书,在更换配置OK后,发现APP停止服务了。所有的请求全部都失败,也就是SSL-PINNING证书到期后会导致APP拒绝服务。改进:
可以将证书的publicKey部分提取到客户端硬编码,每次建立TLS连接时候,匹配公钥。
服务器在更换续费证书时不要更换私钥就行了。推荐需要实现防用户自己抓包的APP采取本方案。
常用SSLpinning绕过方法以及原理介绍
在前面我们已经介绍了SSL pinning的原理以及具体的实现,那么我们想要绕过SSL pinning的话,只需要将证书校验的过程绕过就可以。那么我们可以通过hook的方式来修改证书校验过程,这样就能成功的绕过SSL pinning了。
本人常用且比较熟悉的hook工具有两种,分别是frida和Xposed,这两种工具中都有hook所有https证书校验方法的模块。
其中我使用的比较多的是基于Xposed框架下的Justtrustme模块。该模块对上述所说的https实现证书校验的过程都添加了hook代码,至于具体代码细节我这里就不详细说明了,感兴趣的话可以自己去查看源码
也可以使用国内大神写的太极框架,太极 阳是不需要root的,太极 magic是需要root的。太极框架兼容Xposed框架。https://www.taichi-app.com/#/index