3. 常见隐私泄露场景:明文传输风险、日志记录残留、缓存数据泄露、第三方SDK外发

3.1 现象识别——四类泄露的场景画像

OkHttp 接口的隐私泄露并非单一模式,而是分布在 传输链路、本地持久化、遗留痕迹、跨进程外发 四个维度。下表提供了第一现场的快速识别对照:

泄露类型典型现象常现位置OkHttp 相关组件
明文传输HTTPS 请求体/响应体在抓包中直接呈现未加密内容网络请求/响应 payloadOkHttpClient、SSLSocketFactory
日志残留Logcat 或本地日志文件中出现接口 URL、请求头或 JSON bodyHttpLoggingInterceptor、自建拦截器HttpLoggingInterceptor、自定义 Interceptor
缓存泄露离线或切换账号后仍能访问之前用户的数据HTTP 缓存目录、内存缓存Cache、CacheStrategy、DiskLruCache
第三方SDK外发App 流量监控显示 /ID 被传输至非业务域名第三方 SDK 发起的 OkHttp 请求EventListener、Dns、ConnectionPool

3.2 数据采集——四个维度的取证方法

排查的第一步是确定排查范围。使用以下指令分别获取各维度的证据:

  1. 明文传输取证:使用抓包工具(如 Wireshark、Charles)限制目标 IP,观察 SSL/TLS 握手过程。如果 Client Hello 直接发出 http:// 或不支持 TLS,即可定性。
  2. 日志残留取证:执行 adb logcat | grep -E "OkHttp|HttpLogging" 后重放敏感 API 请求,标记包含手机号、身份证号的日志行。
  3. 缓存泄露取证:在 /data/data/包名/cache/http-cache/ 目录下导出 .0、.1 文件,检查是否包含序列化的敏感响应。
  4. 第三方SDK外发取证:在 OkHttpClient 上注册 EventListener,打印所有 requestHeaders 的 Host 字段,与非业务域名白名单交叉比对。
⚠️ 取证避坑:明文传输和第三方外发需要预先在 OkHttpClient 上添加 EventListener,否则事后无法补采数据。建议排期阶段就在所有 Client 上默认挂载安全监听。

3.3 原因定位——故障树分析

使用下面的故障树逐层解剖,不要跳过中间节点:

隐私泄露(根事件)
├─ 明文传输
│  ├─ 未启用 HTTPS(使用 http:// 或端口 80)
│  ├─ TLS 配置错误(未验证证书、CN 不匹配)
│  └─ 自定义 SSLSocketFactory 信任所有证书
├─ 日志残留
│  ├─ HttpLoggingInterceptor 级别设为 BODY
│  ├─ 自定义拦截器直接打印 request.body()
│  └─ 日志文件未设置过期清理策略
├─ 缓存泄露
│  ├─ Cache 目录权限设置为 MODE_WORLD_READABLE
│  ├─ CacheStrategy 允许缓存带 Authorization header 的响应
│  └─ 未在退出登录时清除 Cache
└─ 第三方SDK外发
   ├─ SDK 内部使用默认 OkHttpClient,未隔离
   ├─ SDK 将  放入全局 Header
   └─ SDK 穿透了 App 的 Dns 拦截器

3.4 修复动作——按场景锁定修复

针对每种根因,执行对应的修复选项:

场景修复前修复后(安全基线)
明文传输new OkHttpClient.Builder().build()显式指定 sslSocketFactory() 并添加 CertificatePinner
日志残留addInterceptor(new HttpLoggingInterceptor().setLevel(Level.BODY))setLevel(Level.HEADERS) 或仅在 Debug 包使用 Level.BASIC
缓存泄露new Cache(cacheDir, 10 * 1024 * 1024)添加 CacheStrategy.FORCE_NETWORK 或退出时 cache.evictAll()
第三方SDK外发SDK 直接引用全局 OkHttpClient为 SDK 构造独立的 OkHttpClient.Builder(),并添加 EventListener 拦截异常域名
🔍 现场典型修复代码 —— 日志级别收紧:
// 错误写法(所有环境下都打印 Body)
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);

// 正确写法(Release 包关闭日志)
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
if (BuildConfig.DEBUG) {
    logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
} else {
    logging.setLevel(HttpLoggingInterceptor.Level.NONE);
}

3.5 复测——隐私泄漏的回归验证清单

  1. 明文复测:再次抓包,确认所有请求均使用 https:// 且 TLSv1.2+。
  2. 日志复测:运行 adb logcat grep 排除所有 body 级别的密钥、身份证号输出。
  3. 缓存复测:退出账号 → 清理缓存 → 离线访问原接口 → 应返回 401 或空数据。
  4. SDK外发复测:在 EventListener 的回调中对非白名单域名 throw new IOException("非法外发"),验证是否触发中断。

3.6 预防——在开发流程中嵌入安全检查点

  • CI 阶段:对 OkHttpClient.Builder 的字节码进行扫描,检测 sslSocketFactory 是否覆盖了默认验证逻辑。
  • Code Review 卡片:合并请求时必须勾选“无明文传输/日志Body输出/缓存/未隔离OKHTTP”。
  • 定期水位线检测:每周扫描一次 HttpLoggingInterceptor 的 Level 配置与全局 Cache 实例的生命周期管理。
✅ 通过性标准:如果四个场景中任意一项的复测结果仍为“失败”,则本次迭代不允许发布到生产环境。
四类隐私泄露场景排查流程图 API请求 明文传输 日志残留 缓存泄露 第三方SDK外发 排查入口:EventListener + 抓包 最终处置:明确修复 → 复测 → 纳入CI/AST检查