安全不是一个你可以"以后再做"的待办事项。它是一种贯穿每一次发布的习惯。经过多年发布 Flutter 应用的经验,我发现大多数安全事件都源于一些小小的疏忽------泄露的密钥、糟糕的存储选择或马虎的依赖关系。这份清单简短、实用,面向各个水平的开发者。在发布前完成这些,你就能避免常见且代价高昂的错误。
1. 绝不硬编码密钥
不要在你的代码仓库中存储 API 密钥、服务账户 JSON 或 OAuth 密钥。
- 使用你的 CI 密钥存储(GitHub Actions Secrets, GitLab CI 变量)在构建时注入。
- 对于设备上的运行时密钥,使用 flutter_secure_storage(iOS 上的 Keychain / Android 上的 Keystore)。
- 确认调试版本不包含生产环境的密钥。
为什么: 源代码泄露是后端被攻破的最简单方式。
2. 强制使用 HTTPS 并考虑证书锁定
- 所有网络调用都要始终使用 HTTPS。对于非常敏感的端点(认证、支付),考虑使用证书锁定(certificate pinning)。
- 使用支持安全上下文的网络库(例如,配置了安全设置的 Dio)或在需要时实现原生的
SecurityContext
。 - 制定 CA 证书轮换计划,以防锁定导致你的应用无法工作。
为什么: 防止在不安全的网络中发生中间人攻击。
3. 强化认证和令牌处理
使用经过验证的认证提供商(Firebase Auth, Auth0)和生命周期短的令牌。
- 将令牌存储在
flutter_secure_storage
中。 - 将令牌刷新逻辑放在服务器端(刷新令牌最好由你的后端处理)。
- 在进行敏感操作之前,在服务器端验证令牌。
为什么: 降低账户被劫持和令牌重放的风险。
4. 保护本地数据和缓存
永远不要在明文的 SharedPreferences
或文件中存储 PII(个人身份信息)、密码或长期有效的令牌。
- 对敏感的本地数据库进行加密,或者避免在本地存储敏感数据。
- 在用户退出登录或长时间不活动后,清除敏感缓存。
为什么: 手机可能会丢失、被盗或被 Root------保护处于静态存储中的用户数据。
5. 最低权限原则
- 只请求你的应用真正需要的权限。
- 从
AndroidManifest.xml
和Info.plist
中移除未使用的权限。 - 在特定场景中向用户解释权限需求(用户体验很重要)。
为什么: 权限越少,受攻击面越小,用户信任度越高。
6. 保护你的后端和 Firebase 规则
- 仅有客户端检查是不够的。
- 对于 Firestore/Storage,编写严格的安全规则并使用 Firebase Emulator 进行测试。
- 对关键操作使用服务器端验证和基于角色的访问控制。
为什么: 配置错误的安全规则是数据泄露的常见原因。
7. 审查和监控依赖项
- 第三方软件包可能会引入漏洞。
- 优先选择维护良好的软件包(有近期提交、活跃的 Issue)。
- 锁定软件包版本,并使用 Dependabot、Snyk 或 GitHub 安全警报来监控 CVE(常见漏洞和暴露)。
- 移除未使用的软件包(使用
flutter pub deps
来检查)。
为什么: 供应链问题是真实存在的------保持依赖项精简并持续监控。
8. 在发布版本中进行代码混淆、压缩和去除调试信息
这会使逆向工程变得更难,并减小应用体积。
- Dart 代码混淆和分离调试信息:
css
flutter build appbundle --release --obfuscate --split-debug-info=./debug-info
-
Android: 启用 R8/ProGuard(minifyEnabled true, shrinkResources true)。
-
iOS: 在发布版本中去除调试符号。
**为什么: **在减小应用体积的同时,保护知识产权和敏感逻辑。
9. 避免记录敏感信息并启用监控
永远不要记录令牌、密码或 PII(个人身份信息)。
- 使用 Crashlytics 或 Sentry 来获取崩溃报告(确保只在发布版本中记录)。
- 清理日志并设置保留策略。
为什么: 日志是常见且容易被忽视的数据泄露源。
10. 制定事故和回滚计划
在出现问题之前做好准备。
- 使用分阶段发布和功能标志来限制影响范围。
- 记录回滚步骤,并准备好热修复路径。
- 拥有联系/通知渠道和一份简单的事故报告模板。
为什么: 快速响应可以减少损失和用户流失。