从服务端证书、本地 Token 存储、通信加密到客户端权限申请与日志脱敏,梳理消息推送系统在敏感数据保护与隐私合规上的工程实践。

软件定制开发团队
"真正有价值的技术内容,应该能帮助客户更快判断方向、预算和落地路径。"
消息推送系统在项目中被视为“功能模块”,安全关注度远低于登录、支付或数据存储。这种低估源于一个常见误解:推送只是把一段文本从服务器发到手机,攻击面小。
实际上,推送系统是 APP 中少数几个需要同时处理服务端证书、客户端持久化 Token、设备标识符和用户隐私数据的模块。任何一个环节出现漏洞,都会导致敏感数据泄露、推送被劫持,甚至被用于大规模钓鱼攻击。
从工程角度看,推送系统安全涉及四个层面:服务端凭证管理、本地存储保护、通信加密与证书固定、客户端权限与隐私合规。下面逐一拆解。
无论是 iOS 的 APNs(Apple Push Notification service)还是 Android 的 FCM(Firebase Cloud Messaging),服务端都需要持有长期有效的凭证。iOS 使用 .p8 密钥文件或 .p12 证书,Android 使用服务器密钥(Server Key)。这些凭证一旦泄露,攻击者可以直接向用户设备发送伪造推送。
常见的不安全做法包括:
凭证应存储在专用的密钥管理服务(KMS)或环境变量中,运行时动态读取。文件权限设置为仅服务进程可读。对于 .p8 密钥,建议使用托管密钥服务(如 AWS Secrets Manager、HashiCorp Vault)自动轮换。
证书过期是另一个容易被忽略的点。APNs 的 .p12 证书有效期为一年,到期后未更新会导致推送全部失败。建议在证书到期前 30 天设置监控告警,并提前准备新证书的签发流程。
开发、测试和生产环境应使用不同的推送凭证。开发环境使用 Sandbox APNs,生产环境使用 Production APNs。混淆证书会导致开发阶段推送测试正常,但上线后失效,或者反过来。
推送 Token 是设备向推送服务注册后获得的唯一标识。服务端需要存储 Token 才能向特定设备推送。Token 本身不包含用户个人信息,但结合服务端业务数据(如用户 ID),可以关联到具体用户。
存储 Token 的风险在于:
iOS 使用 Keychain 存储推送 Token 是标准做法。Keychain 的数据在设备锁定时加密,且受硬件保护。部分开发者为图方便将 Token 存入 UserDefaults,这是不安全的。UserDefaults 数据以明文形式存储在应用沙盒中,备份到 iCloud 后更容易泄露。
Android 上,Token 通常由 FCM SDK 管理并回调给应用。应用需要将 Token 持久化以便后续发送。建议存储在 EncryptedSharedPreferences(AndroidX Security 库)中,而不是普通 SharedPreferences。EncryptedSharedPreferences 使用 AES-256 加密,密钥由 Android Keystore 保护。
服务端存储 Token 时应与用户 ID 绑定,但 Token 本身应视为敏感字段。数据库中的 Token 列建议加密存储,至少使用哈希加盐(不推荐,因为推送需要原文)。更实用的做法是使用对称加密(如 AES-256-GCM),密钥存储在 KMS 中。
Token 失效处理:设备卸载应用、重置推送服务或系统更新后,Token 会变更。服务端应定期清理无效 Token,避免向无效设备重复推送。建议每次推送后检查错误码,对 InvalidToken 或 Unregistered 状态码及时删除记录。
APNs 和 FCM 均强制要求 TLS 1.2 以上版本。服务端与推送网关之间的连接应使用证书验证,并启用证书固定(Certificate Pinning)。
证书固定的目的是防止中间人攻击:即使攻击者伪造了 CA 签发的证书,只要服务端只信任预置的证书指纹,连接仍然会被拒绝。对于 Java 服务端,可以通过自定义 TrustManager 实现;对于 Node.js,可以使用 tls.connect 的 ca 选项指定证书。
证书固定需要预留更新机制。证书更换时,如果客户端只固定了旧证书,服务端会无法连接。建议在代码中同时固定主证书和备用证书,或通过配置中心动态下发证书指纹。
推送通知的 payload 在传输过程中是加密的(TLS),但到达设备后,系统会解密并显示在通知栏。如果推送内容包含敏感信息(如交易金额、验证码),建议在应用层额外加密。
做法是:服务端使用对称密钥加密 payload 中的敏感字段,客户端收到通知后,在应用内解密再显示。密钥可以通过其他安全通道(如登录接口)下发到客户端,存储在 Keychain 或 EncryptedSharedPreferences 中。
需要注意,推送通知的标题和正文在 iOS 和 Android 的通知栏中可能被其他应用或系统截获(例如通过 NotificationListenerService)。因此,即使应用层加密,也不应将高度敏感的数据直接放在推送通知中。更好的做法是推送一条“您有新的消息”,用户打开应用后再从服务端拉取实际内容。
iOS 和 Android 均要求用户授权才能接收推送通知。权限申请时机直接影响授权率。常见错误做法:
工程实践建议:
Android 13(API 33)开始,通知权限从自动授予改为运行时权限。应用需要在 AndroidManifest 中声明 POST_NOTIFICATIONS 权限,并在运行时请求。这与 iOS 的权限模型趋于一致。
适配时需要注意:
推送权限本身不涉及敏感个人信息,但推送 Token 和设备标识符(如 IDFV、Android ID)的收集需要遵守隐私政策。在应用首次启动时,应在隐私协议中明确说明推送系统收集哪些数据、用于什么目的。
对于 GDPR 或中国《个人信息保护法》覆盖的用户,推送权限的申请应在用户同意隐私协议之后进行。如果用户拒绝隐私协议,不应发起权限请求,也不应调用推送 SDK 的注册方法。
开发调试时,日志中经常出现:
这些日志如果落入第三方(如日志聚合服务、崩溃收集工具),可能构成数据泄露。
在日志输出前,对敏感字段进行脱敏处理:
生产环境的日志级别应设置为 WARN 或 ERROR,避免将调试日志写入文件。日志文件应设置定期轮转和自动清理,保留周期建议不超过 30 天。
推送 SDK(如 Firebase、极光、个推)通常自带日志输出。集成时应在发布版本中关闭 SDK 的调试日志。以 Firebase 为例,可以通过 FirebaseMessaging.getInstance().setLogLevel(LogLevel.NONE) 关闭。对于无法通过 API 控制的 SDK,可以在 ProGuard 或 R8 规则中移除日志相关类。
iOS 方面,App Store 审核会检查应用是否在推送内容中包含敏感数据,以及是否在未获得用户同意的情况下收集推送 Token。如果应用使用推送进行广告营销,必须在隐私协议中说明。
Android 方面,Google Play 对推送权限的申请时机有明确要求:应用不得在启动时立即请求通知权限,必须与功能关联。对于国内 Android 市场,部分应用商店(如华为、小米)要求推送服务必须在隐私协议同意后才能初始化。
在项目上线前,建议逐项检查:
如果服务端推送凭证泄露,攻击者可以向所有 Token 发送伪造推送。应对措施:
Token 泄露不等于用户数据泄露。但攻击者可以利用 Token 向用户发送钓鱼推送。应对措施:
使用第三方推送服务(如极光、个推、腾讯移动推送)时,需要评估其安全能力:
SystemDo 在多个 APP 项目中集成过第三方推送服务,经验是:在选型阶段要求对方提供安全白皮书,并在合同中明确数据存储位置和泄露责任条款。对于金融、医疗等强监管行业,自建推送通道并配合应用层加密是更稳妥的选择。
推送系统安全不是单一的技术实现,而是贯穿服务端凭证管理、本地存储、通信加密、权限申请和日志处理的全链路工程实践。关键判断:
这些实践适用于大部分 APP 项目,具体实现细节以各平台官方文档为准。
继续了解企业数字化、SEO / GEO、AI 自动化和软件定制开发中的常见问题。