网络安全通信:HTTPS配置、SSL Pinning实现、证书校验、WebView安全配置
大家好,我是你们的老朋友。今天我们来聊聊网络安全通信这个话题。说实话,这是Android安全开发里最基础也最容易翻车的一环。我见过太多App因为HTTPS配置不当,直接被中间人攻击扒了个精光。嗯,咱们一步步来。
一、HTTPS配置:别只改个URL就完事
很多人觉得HTTPS配置就是把http://改成https://。我刚开始做开发时也这么想,直到有一次线上事故——证书过期了,App直接崩了。从那以后,我再也不敢轻视HTTPS配置。
Android里配置HTTPS,核心是处理好HttpsURLConnection或者OkHttp。我个人习惯用OkHttp,因为它更灵活。
// 基础HTTPS配置示例
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.sslSocketFactory(sslContext.getSocketFactory(), trustManager)
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// 这里别偷懒,一定要校验hostname
return hostname.equals("api.yourdomain.com");
}
})
.build();
这里有个坑:hostnameVerifier千万别直接返回true。我曾经在代码审查时见过有人这么写,理由是“测试方便”。结果上线后,用户数据全裸奔。你想想看,这多可怕。
二、SSL Pinning:给证书加把锁
SSL Pinning说白了就是固定证书。为什么要做这个?因为系统自带的CA证书链可能被攻破,或者用户手机里被植入了恶意CA证书。这时候,就算你用的是HTTPS,中间人也能解密你的流量。
我建议用证书哈希值做Pinning,而不是直接固定证书文件。为什么呢?因为证书有有效期,到期换证时你只需要更新哈希值,不用重新发版。
核心思路:客户端只信任我们指定的证书或公钥,其他的一概不认。
// OkHttp实现SSL Pinning
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("api.yourdomain.com",
"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.add("api.yourdomain.com",
"sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")
.build();
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
小技巧:建议至少固定两个哈希值——一个当前证书,一个备用证书。这样换证时不会断服务。
三、证书校验:别只依赖系统
系统自带的证书校验其实挺靠谱的,但还不够。为什么?因为Android 4.2以下版本存在证书校验漏洞,而且部分厂商定制ROM可能修改了CA证书列表。
我建议自己做一层校验。具体做法是:
- 把服务端证书打包进App资源文件
- 建立自定义TrustManager
- 在校验时对比本地证书和服务端证书
// 自定义证书校验
private static SSLSocketFactory createSSLSocketFactory() {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = context.getResources().openRawResource(R.raw.server_cert);
Certificate ca = cf.generateCertificate(caInput);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
注意:千万别把私钥打包进App!我见过有人把.p12文件直接放assets目录里,这等于把家门钥匙送给小偷。
四、WebView安全配置:最容易忽视的漏洞
WebView的安全配置,说实话是Android安全里最容易被忽视的一环。很多App只是简单加载个网页,结果被XSS、CSRF打得满地找牙。
我总结了几条必须遵守的规则:
- 禁用JavaScript:除非绝对必要,否则关掉JS
- 禁用文件访问:防止恶意网页读取本地文件
- 禁用地理定位:保护用户隐私
- 使用HTTPS:WebView默认支持,但别忘了校验
// WebView安全配置示例
WebView webView = findViewById(R.id.webview);
WebSettings settings = webView.getSettings();
// 安全配置
settings.setJavaScriptEnabled(false); // 默认关掉JS
settings.setAllowFileAccess(false); // 禁止文件访问
settings.setAllowContentAccess(false); // 禁止Content Provider访问
settings.setGeolocationEnabled(false); // 禁止地理定位
settings.setMixedContentMode(
WebSettings.MIXED_CONTENT_NEVER_ALLOW); // 禁止混合内容
// 如果必须用JS,至少做这些
if (needJavaScript) {
settings.setJavaScriptEnabled(true);
settings.setAllowFileAccessFromFileURLs(false);
settings.setAllowUniversalAccessFromFileURLs(false);
}
避坑指南:我曾经遇到一个案例,App用WebView加载了第三方H5页面,结果那个页面里嵌了恶意JS,直接通过JavaScriptInterface偷走了用户的登录Token。从那以后,我对WebView的JavaScriptInterface格外谨慎。
五、知识体系总览
说了这么多,咱们用一张图来总结一下网络安全通信的核心要点:
这张图把咱们今天讲的内容串起来了。你看,从HTTPS配置到SSL Pinning,再到证书校验和WebView安全,其实核心就一句话:不信任任何未经验证的连接。
好了,今天就聊到这儿。网络安全通信这块,说白了就是“信任”两个字——你信谁、怎么信、信到什么程度。把这些想清楚了,代码自然就安全了。
公众号:蓝海资料掘金营,微信deep3321