Android防止Charles等工具抓包

最近在做一个金融项目,对于安全性的要求比较高。安全部门针对移动端做了安全漏洞的检测,找到了一些安全问题。其中有一条是,移动端APP可以被第三方抓包工具进行抓包。比如Charles。这样的话就会将一些接口信息暴露给外部分析,进而通过一定的手段进行中间人攻击,使用户遭受经济损失。针对这个问题,我们来给出一些解决方案。

如果是防止Charles等抓包工具进行抓包,我们可以通过检测网络设置,查看是否设置了代理,如果设置了代理,则不进行网络请求。具体做法可参考Android防止apk被抓包工具抓包
但是这样做的话,只是一些简单的防御手段,可能会误伤一些需要使用网络代理的用户。而且可以通过其他手段,比如在WiFi上做手脚等,来实现对网络的抓包和篡改。那么有没有其他办法来解决这个问题呢?如果是https的话,那么也许我们还可以采取以下手段。
https是在http的基础上扩展而来的,底层有tsl和ssl协议来作为安全传输的保证。客户端可以通过对https证书的校验,来保证通信的安全性。

  • 内置ca证书,不同框架的代码略有不同,以下代码以OKHttp为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static SSLSocketFactory genSSLFactory() {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
InputStream certificate = HuobiApplication.getInstance().getAssets().open("https.cer");
String certificateAlias = Integer.toString(index++);

keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));

if (certificate != null)
certificate.close();

SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null,
trustManagerFactory.getTrustManagers(),
new SecureRandom());

return sslContext.getSocketFactory();
} catch (Exception e) {
DebugLog.e(e);
return null;
}
}

将生成的Factory赋给OKHttpClient即可。

  • 如果用户不愿意将证书内置到APP中,也可以将证书转为字符串,定义在APP中,使用时转化成流即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
keytool -printcert -rfc -file srca.cer

-----BEGIN CERTIFICATE-----
MIICmjCCAgOgAwIBAgIIbyZr5/jKH6QwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ04xKTAn
BgNVBAoTIFNpbm9yYWlsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRTUkNBMB4X
DTA5MDUyNTA2NTYwMFoXDTI5MDUyMDA2NTYwMFowRzELMAkGA1UEBhMCQ04xKTAnBgNVBAoTIFNp
bm9yYWlsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRTUkNBMIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQDMpbNeb34p0GvLkZ6t72/OOba4mX2K/eZRWFfnuk8e5jKDH+9BgCb2
9bSotqPqTbxXWPxIOz8EjyUO3bfR5pQ8ovNTOlks2rS5BdMhoi4sUjCKi5ELiqtyww/XgY5iFqv6
D4Pw9QvOUcdRVSbPWo1DwMmH75It6pk/rARIFHEjWwIDAQABo4GOMIGLMB8GA1UdIwQYMBaAFHle
tne34lKDQ+3HUYhMY4UsAENYMAwGA1UdEwQFMAMBAf8wLgYDVR0fBCcwJTAjoCGgH4YdaHR0cDov
LzE5Mi4xNjguOS4xNDkvY3JsMS5jcmwwCwYDVR0PBAQDAgH+MB0GA1UdDgQWBBR5XrZ3t+JSg0Pt
x1GITGOFLABDWDANBgkqhkiG9w0BAQUFAAOBgQDGrAm2U/of1LbOnG2bnnQtgcVaBXiVJF8LKPaV
23XQ96HU8xfgSZMJS6U00WHAI7zp0q208RSUft9wDq9ee///VOhzR6Tebg9QfyPSohkBrhXQenvQ
og555S+C3eJAAVeNCTeMS3N/M5hzBRJAoffn3qoYdAO1Q8bTguOi+2849A==
-----END CERTIFICATE-----

其中keytool为jdk自带命令

  • 如果觉得以上做法还是麻烦,对于证书为CA官方证书的同学,我们可以有一些更加简单的方法。OKHttp为我们提供了一个方法 OKHttpClient.certificatePinner 。具体代码如下:
1
2
3
4
5
CertificatePinner pinner = new CertificatePinner.Builder()
.add("mobile.baidu.com", "sha256/8TjMN8qohudrWw5QZ6yKJWdrQSnbI1dMESNBexomY7M=")
.add("m.baidu.com", "sha256/8TjMN8qohudrWw5QZ6yKJWdrQSnbI1dMESNBexomY7M=")
.add("www.baidu.com", "sha256/8TjMN8qohudrWw5QZ6yKJWdrQSnbI1dMESNBexomY7M=")
.build();

对于证书对应的sha256的值,可以在ssllabs这个网站上查询。
关于这个接口的具体使用方式,大家可以查看OKHttp的文档,这里就不再赘述了。
需要提醒的一点是,网址前边不要加 https:// 的字样,不要问我为什么知道,这是我付出了一个小时血的代价才明白的。

最后需要提醒大家的一点就是,对于客户端锁定证书的操作,大家一定要慎重,因为一旦服务器更换了证书,那么这个锁定将会导致客户端请求API失败,后果很严重,大家要谨慎。