网络安全通信: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证书列表。

我建议自己做一层校验。具体做法是:

  1. 把服务端证书打包进App资源文件
  2. 建立自定义TrustManager
  3. 在校验时对比本地证书和服务端证书
// 自定义证书校验
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安全配置 OkHttp配置 · Hostname校验 证书哈希固定 · 备用证书策略 自定义TrustManager · 本地证书对比 禁用JS/文件访问 · 混合内容控制 核心原则:不信任任何未经验证的连接 多层校验 + 最小权限 + 持续监控

这张图把咱们今天讲的内容串起来了。你看,从HTTPS配置到SSL Pinning,再到证书校验和WebView安全,其实核心就一句话:不信任任何未经验证的连接

好了,今天就聊到这儿。网络安全通信这块,说白了就是“信任”两个字——你信谁、怎么信、信到什么程度。把这些想清楚了,代码自然就安全了。


公众号:蓝海资料掘金营,微信deep3321