跳转至

Android 应用程序安全清单

阶段 0:治理、环境和 CI/CD

实施 CI 门控 (CI gates)

  • 在每次 PR 上运行静态检查:ktlint / Android Lint、单元/UI 测试、依赖项审计(Gradle、SBOM)。
  • 在出现高/关键 SAST/SCA/DAST 漏洞时阻止发布;在发布分支上保证零 lint 错误。

机密处理

  • 源代码、BuildConfig、AndroidManifest.xml、资源或日志中没有机密。
  • 使用环境/机密管理器(例如,Gradle 机密、云 KMS);在分析 (analytics) 中隐去机密。
  • 定期以及在怀疑泄漏时轮换密钥/令牌。

证据和可追溯性

  • 归档每个制品的扫描报告、SBOM、构建日志和发布说明。

扫描自动化

  • 在 CI/CD 中集成移动 SAST/DAST/SCA 平台,在每个 PR 和预发布中运行,并在发现高/关键问题时自动使门控失败。

阶段 1:威胁建模 (Threat modeling)、数据分类和清单

  • 对数据(PII、凭据、令牌、支付)进行分类,并映射收集、处理、存储(SharedPreferences、文件、数据库、Keystore)和传输。
  • 识别信任边界(设备、应用程序沙箱、Keystore、IPC/Binder、WebView、本地库、后端 API)。
  • 清点第三方 SDK;首选维护良好、已签名、权限最小的 SDK;尽量减少数量。

阶段 2:构建和配置强化 (Gradle + Manifest)

Gradle/发布设置 (Release settings)

  • 使用带有代码缩减/混淆的发布版本(minifyEnabled trueshrinkResources true)。
  • 从已发布 APK/AAB 中剥离调试符号。
  • 将映射文件保留在服务器端。
  • 从发布版本中排除测试/调试库。
  • 从生产构建中删除开发端点和功能标志 (feature flags)。

签名和权限

  • 保护签名密钥(硬件支持的密钥库、受限访问)。
  • 使用专用的发布签名配置。
  • 仅在 AndroidManifest.xml 中声明必要的权限。
  • 避免过度危险的权限 (dangerous permissions)。

网络安全配置

  • 使用 networkSecurityConfig 实施 TLS。
  • 不允许明文流量(android:usesCleartextTraffic="false")。
  • 仅在必要时并在限定时间内使用域配置覆盖。

Manifest 卫生

  • 除非严格要求,否则不要设置组件 exported="true";导出时使用权限进行限制。
  • 删除调试标志(发布版本中 android:debuggable="false",除非需要并带有适当的加密,否则不使用 allowBackup="true")。
  • 为所有运行时权限 (runtime permissions) 提供准确的权限理由说明文本(在应用程序内)。

阶段 3:本地存储和 Keystore 安全

Keystore 使用

  • 将长效密钥存储在 Android Keystore 中,并提供最强可行的保护(如果有的话采用硬件支持,如果 UX 允许则采用用户身份验证)。
  • 对高度敏感的密钥使用 setUserAuthenticationRequired(true);在适当的情况下利用生物识别/锁屏来控制操作。
  • 在注销或更换设备时删除被 Keystore 包装的机密。
  • 考虑对高风险令牌进行设备绑定 (device-binding)。

文件、SharedPreferences 和数据库

  • 避免将机密或敏感的 PII 以明文形式存储在文件或 SharedPreferences 中。
  • 加密敏感数据库/文件(例如 SQLCipher 或等效的文件加密)。
  • 使用应用程序私有存储(MODE_PRIVATE)。
  • 在受支持的地方应用文件保护标志。
  • 确保备份不包含未加密的机密。

缓存和派生数据

  • 避免将 PII 持久化在缓存/临时文件中。
  • 在注销时以及作为后台处理的一部分(如果合适)清除缓存/临时文件。
  • 即使是暂时的,也不要将机密写入日志或 SharedPreferences

阶段 4:隐私保障(UI、剪贴板、屏幕截图)

  • 在应用程序切换器屏幕截图中隐去敏感的 UI(例如,在敏感的 Activities/Views 上使用 FLAG_SECURE)。
  • 对于高度敏感的流程,请考虑额外的屏蔽或单独的“安全模式” Activities。
  • 避免在应用程序启动时读取剪贴板;绝不将密码/长期令牌放在剪贴板上。

阶段 5:网络安全和 TLS/固定 (pinning)

HTTPS/TLS 实施

  • 强制对受信任主机的 HTTPS;拒绝无效的证书/主机名;禁用 HTTP 回退 (fallbacks)。
  • 配置严格的超时、带抖动 (jitter) 的指数退避,并在 TLS 错误时采取故障关闭 (fail closed)。

证书固定 (Certificate pinning)

  • 优先使用 SPKI SHA-256 固定;为每个主机包含至少一个备用固定。
  • 通过 OkHttp / 自定义 TrustManagernetworkSecurityConfig 实施固定。
  • 根据 MITM 场景和过期/轮换的叶子证书进行验证。
  • 计划安全的固定轮换;在固定失败时包含遥测。

Cookies 和令牌

  • 对于 Web 流程,使用安全、HttpOnly、SameSite cookies。
  • 避免设备上的长期不记名令牌 (bearer tokens)。
  • 在敏感会话后清除 cookies 和 WebView 数据。

阶段 6:身份验证、授权和会话

身份验证流程

  • 为公共客户端使用带有 PKCE 的 OAuth2/OIDC;绝不能在应用程序中嵌入客户端机密。
  • 首选基于系统/浏览器的身份验证(Chrome Custom Tabs / 默认浏览器)进行登录,而不是应用程序内的 WebView。

令牌

  • 使用短期访问令牌 (access tokens)。
  • 仅将刷新令牌 (refresh tokens) 存储在绑定到 Keystore 密钥的加密存储中。
  • 在注销、更换设备和出现可疑活动时轮换并使令牌失效。
  • 在适当的情况下将会话绑定到设备风险 (device risk)。

授权

  • 在服务器端实施授权;将客户端检查仅视为建议性参考。
  • 跨应用程序使用的所有后端 API 测试并预防 IDOR/BOLA 漏洞和特权升级。

阶段 7:WebView 强化

  • 仅在需要时使用 WebView;尽可能首选浏览器进行身份验证和付款。

默认值

  • 除非严格必要,否则禁用 JavaScript。
  • 如果启用 JavaScript,请限制功能并禁用 setAllowFileAccessFromFileURLssetAllowUniversalAccessFromFileURLs
  • 不要在暴露敏感方法的对象上启用 addJavascriptInterface,特别是在 API < 17 上。
  • 强制执行导航允许列表(主机/路径);拦截 file://javascript: 和不受信任的方案。
  • 尽可能为敏感会话使用非持久性 WebView 存储。
  • 在注销时清除 cookies、缓存和历史记录。

内容安全

  • 仅通过 HTTPS 提供第一方 Web 内容。
  • 不允许混合内容(MIXED_CONTENT_NEVER_ALLOW)。
  • 确保远程内容的完整性经过检查和控制(除非严格必要,否则不使用不受信任的第三方脚本)。

  • 首选 App Links(已验证的链接)以进行深层链接;验证传入链接的来源和参数。

自定义方案和意图 (intents)

  • 如果使用自定义方案或隐式意图,请确保唯一性并对所有传入数据进行可靠的验证。
  • 使用权限或显式意图保护导出的 Activities/Services/BroadcastReceivers;避免过于宽泛的意图过滤器。
  • 通过检查调用者身份和参数,防御意图欺骗 (intent spoofing)、URL 劫持和开放重定向。

阶段 9:逆向工程和防篡改能力 (tampering)

减少静态泄露

  • 从发布构建中删除调试构建、详细日志、调试菜单和测试端点。
  • 混淆和缩减代码(ProGuard/R8)和资源。
  • 避免在资源或本地库中留下有意义的字符串(API 密钥、机密)。

环境检查

  • 检测 root/钩子/调试器指示器(常见 root 路径、可写的系统目录、检测框架)并记录信号。
  • 在维持 UX 的同时,根据风险按比例响应(减少功能、附加验证或阻止)。

篡改测试 (Tamper testing)

  • 确保重新打包或重新签名的应用程序在完整性检查或服务器证明(例如,安全/证明 API)中失败。
  • 将客户端信号视为后端的提示;绝不能作为唯一的安全控制手段。

阶段 10:密码学和随机性

  • 使用平台提供的加密原语(Java Cryptography Architecture、Android Keystore);不要构建自定义算法。
  • 对认证加密使用现代 AEAD 模式,例如 AES-GCM/ChaCha20-Poly1305。
  • 使用成熟的密钥派生函数 (KDF)(PBKDF2/HKDF),配合适当的参数和盐 (salts)。
  • 使用安全的 RNG(SecureRandom 或平台 API)生成密钥/初始化向量 (IVs)。
  • 绝不重复使用 IV/随机数 (nonces)。
  • 将密钥保留在 Keystore 或处于静态加密状态。

阶段 11:权限、通知和标识符

  • 要求最小权限,在使用的地点,并提供清晰的对用户有价值的情境说明。

隐私相关权限

  • 将对位置、联系人、摄像头、麦克风、存储和电话状态的访问限制为严格必要。
  • 优雅地处理拒绝和撤销。
  • 避免对用户施加压力的黑暗模式 (dark patterns)。

标识符和追踪

  • 首选应用程序范围或可重置的标识符;避免建立跨应用程序的持久指纹识别 (fingerprinting)。
  • 仅根据平台策略使用广告或分析 ID。
  • 绝不使用禁止的硬件标识符进行跟踪。

通知

  • 最大程度减少通知内容中的敏感数据。
  • 尽可能使用通用措辞并在锁定屏幕上隐藏详细信息。

阶段 12:日志记录、分析 (analytics) 和崩溃报告

  • 使用带严重级别的结构化日志记录;在发布版本中禁用详细/调试日志记录。
  • 从日志、分析事件和崩溃报告中隐去 PII 和机密。
  • 尽可能聚合和采样分析数据。
  • 确保混淆构建的符号/映射文件安全地存储在服务器端,以用于解除崩溃混淆。
  • 不要将符号/映射文件发送在应用程序内。

阶段 13:测试、DAST 和运行时验证

流量拦截测试

  • 验证对 MITM(无效证书、主机名不匹配、不受信任的 CA)的抵抗力。
  • 验证在配置了固定 (pinning) 的地方可以阻止拦截。

运行时检查

  • 确认没有机密/PII 出现在日志中。
  • 验证敏感数据在静态时已加密。
  • 审查运行时的 WebView 行为(导航、JS、存储)。
  • 审查运行时的本地存储行为(文件、SharedPreferences、数据库)。
  • 验证在预期的地方使用了由 Keystore 支持的密钥。
  • 验证 Keystore 密钥的访问控制行为是否符合设计。

API 滥用测试

  • 测试速率限制 (rate limiting)。
  • 测试重放保护 (replay protection)。
  • 测试分页边界。
  • 测试批量赋值 (mass assignment)。
  • 测试后端 API 的错误消息卫生。

动态分析

  • 运行移动 DAST,同时执行核心流程以发现传输错误配置、WebView 问题、存储泄漏和日志记录问题。
  • 导出 DAST 的证据用于分类和重新测试。

阶段 14:SBOM 和证据

SBOM 和依赖项

  • 为所有 Gradle 模块和嵌入式 SDK 生成 SBOM。
  • 跟踪 CVE 并定期更新依赖项。
  • 将监控依赖项运行状况作为发布就绪状态的一部分。
  • 将监控许可证合规性作为发布就绪状态的一部分。

证据包

  • 将扫描工件附加到发布记录。
  • 将 SBOM 附加到发布记录。
  • 将 manifest 差异附加到发布记录。
  • 将权限和组件导出列表附加到发布记录。
  • 将自动化/手动测试报告附加到发布记录。

阶段 15:报告和标准对齐

报告

  • 提供执行摘要、范围/方法、带有 PoC/证据的详细发现结果、风险评级、修复指导和重新测试结果。

标准映射(示例)

  • ARCH:阶段 1–2
  • PLATFORM:阶段 2、7–8、11
  • STORAGE:阶段 3–4
  • CRYPTO:阶段 10
  • AUTH:阶段 6
  • NETWORK:阶段 5
  • CODE:阶段 2、9、12
  • RESILIENCE:阶段 9

从业人员快速参考

  • Manifest/Network:发布中没有明文流量;严格的 networkSecurityConfig 域;没有强有力的理由避免导出组件。
  • 机密和存储:使用 Android Keystore 获取长效密钥;加密数据库/文件;绝不将机密存储在 SharedPreferences 或日志中。
  • Network:仅 HTTPS;针对关键端点具备备份功能的证书/SPKI 固定;验证 MITM 尝试失败。
  • Auth:OIDC + PKCE;短期访问令牌;将刷新令牌保留在加密存储中;服务器端授权;测试 IDOR/BOLA。
  • WebView:首选浏览器进行身份验证;如果使用 WebView,默认关闭 JS,限制界面,列入导航允许列表,敏感流程使用非持久存储。
  • 隐私:为敏感屏幕使用 FLAG_SECURE;避免剪贴板机密;最小化权限并提供清晰的解释。
  • 发布卫生:混淆和缩减;删除调试日志/菜单/测试端点;每次发布时验证权限和导出的组件。