网络安全通信:HTTPS配置、SSL Pinning实现、证书校验、WebView安全配置
大家好,我是你们的Android安全讲师。今天我们来聊聊网络安全通信这块硬骨头。
说实话,我见过太多App因为网络通信没做好而出事的案例。有一次我接手一个金融类App的审计,发现它居然还在用HTTP明文传输用户的银行卡号。我当时就愣住了——这等于把用户的密码写在明信片上寄出去啊。
好,我们直接进入正题。网络通信安全,说白了就是解决三个问题:通信内容不被偷看、不被篡改、对方确实是你要找的人。Android上怎么做?我们一个一个来看。
一、HTTPS配置:别再用HTTP了
HTTPS不是可选项,是必选项。我个人习惯在项目一开始就把网络层强制为HTTPS。
在Android中,配置HTTPS其实很简单。你需要在AndroidManifest.xml中声明网络安全配置:
<application
android:networkSecurityConfig="@xml/network_security_config"
...>
然后在res/xml/network_security_config.xml中写:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
这里cleartextTrafficPermitted="false"就是禁止明文HTTP流量。嗯,这一步做完,你的App就不会偷偷走HTTP了。
10.0.2.2(模拟器)或localhost开启明文,但上线前一定要删掉。
二、SSL Pinning:防中间人攻击的杀手锏
HTTPS就够了吗?不够。你想想看,如果用户的设备上被安装了恶意CA证书(比如某些公司监控员工手机),那HTTPS照样能被中间人攻击。
SSL Pinning就是解决这个问题的。它让App只信任你指定的证书或公钥,而不是系统里所有的CA证书。
我曾经在一个银行项目中,因为没做SSL Pinning,测试人员用Fiddler就轻松抓到了所有API请求。从那以后,我再也不敢省略这一步。
实现方式有两种:
- 证书Pinning:把服务器证书的哈希值硬编码到App里
- 公钥Pinning:只固定公钥,证书过期时不用更新App
我用OkHttp举个例子:
// 获取证书的SHA-256哈希值
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("api.yourdomain.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
这里有个坑——备份证书。我建议你至少固定两个证书:当前证书和备用证书。万一证书轮换,你的App不会直接挂掉。
openssl s_client -connect api.yourdomain.com:443 -showcerts来获取服务器证书的哈希值。
三、证书校验:别让代码骗了你
有些开发者为了省事,会写一个TrustAllCertificates的类。我见过最离谱的代码是这样的:
// ❌ 千万不要这样做!
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]; }
}
};
这段代码等于告诉系统:任何证书我都信。你想想看,这跟没锁门有什么区别?
正确的做法是使用系统默认的TrustManager,或者自己实现校验逻辑:
// ✅ 正确的证书校验
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
如果你需要自签名证书,可以把证书放到res/raw目录下,然后用自定义的TrustManager加载它。
四、WebView安全配置:最容易忽视的漏洞
WebView是Android安全的重灾区。我审计过的App里,十个有八个WebView配置有问题。
最常见的问题有三个:
- 允许JavaScript与Native交互(addJavascriptInterface)
- 允许加载HTTP内容
- 允许文件访问
来看一个安全的WebView配置:
WebView webView = findViewById(R.id.webview);
WebSettings settings = webView.getSettings();
// 关闭文件访问
settings.setAllowFileAccess(false);
settings.setAllowFileAccessFromFileURLs(false);
settings.setAllowUniversalAccessFromFileURLs(false);
// 只允许HTTPS
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
}
// 谨慎使用JavaScript
settings.setJavaScriptEnabled(true); // 只在必要时开启
// 不要随意添加JavaScript接口
// webView.addJavascriptInterface(new JsBridge(), "bridge"); // ❌ 除非你非常清楚风险
还有一个容易被忽略的点:WebView的证书校验。默认情况下,WebView使用系统的证书链。但如果你用了自定义的WebViewClient,一定要重写onReceivedSslError方法:
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
// ❌ 不要调用 handler.proceed()
// ✅ 应该调用 handler.cancel()
handler.cancel();
}
});
我曾经见过一个电商App,WebView里onReceivedSslError直接调了handler.proceed()。结果用户连到公共WiFi时,所有购物信息都被抓走了。
知识体系总览
下面这张图帮你理清今天讲的内容:
你看,这四个方面其实是环环相扣的。HTTPS是基础,SSL Pinning加固了信任链,证书校验防止了代码层面的绕过,WebView安全则堵住了UI层的漏洞。
最后说一句:安全不是一次性配置,而是持续的过程。每次更新依赖库、每次更换服务器证书,都要重新检查这些配置是否还生效。
- 你的App是否完全禁止了HTTP?
- SSL Pinning是否配置了至少两个备用证书?
- 有没有任何地方使用了TrustAllCertificates?
- WebView的onReceivedSslError是否调用了cancel()?