前言:接到一个朋友的公司的求助。该公司是从传统行业转型成互联网公司,产品经历较为曲折,目前出现代码维护困难、产品发展出现瓶颈等问题。希望我前去帮助审查代码、给出一些优化建议。
本文的主题是审查过程中较为明显的问题:
为什么不建议前端(APP)保存账号密码数据?
注:由于保密性不透露具体公司、人员等重要信息,如有雷同请勿对号入座。
问题场景:
该产品实现APP拥有“游客访问”功能,但APP实现方式是每次通过网络请求来获取"游客账号密码"缓存到手机本地的加密数据库中,每次用户使用“游客访问”功能实际是从手机本地加密数据库中随机取一个"游客账号密码"来登录。
需求分析:
- 首先不要着急去吐槽,我们需要识别一下该功能的真实需求是什么。
- 经过与该公司产品经理的讨论得到对方的真实需求是“希望游客访问的数据是真实、有效的用户数据”。
- 我们再来了解一下为什么当时要这样实现。得出的原因是服务端建立了几个真实账户且数据保证真实、有效,而这些账户又不能给每个用户,因此只能通过接口让APP主动获取缓存在手机本地使用。
OK,我们了解前因后果后,下面针对该实现方案进行问题分析。
问题分析:
- 接口被滥用:任何人或脚本都可以无限制地调用这个接口获取游客账号密码。攻击者可以编写脚本进行批量请求,迅速耗尽可用的游客账号池。
- 虚假请求:攻击者可以利用这些账号进行批量登录操作,导致大量虚假请求,消耗服务器资源。
- 逆向工程和数据库破解风险:即使数据库使用了加密(如 SQLCipher),攻击者依然可以通过逆向工程、提权等方式获取数据库文件,并进行暴力破解。特别是在 Android 设备被 Root 后,安全性会大幅下降。
- 账号滥用风险:由于是从本地随机取出游客账号密码,无法保证一个账号不会被多次使用。如果攻击者获取了本地数据库中的账号密码,可以利用这些游客账号批量进行恶意操作(如爬虫、滥用 API 资源),从而影响系统稳定性。
- 账号管理复杂性:单点登录、同步的问题。游客账号是随机从本地数据库中获取,无法保证一个游客账号仅被一个设备或用户使用,这会导致服务器端出现并发冲突,尤其是在需要进行数据同步时,会增加维护复杂度。
- 账号失效问题:如果某个游客账号因为滥用或其他原因被封禁,那么本地缓存的账号数据就需要实时更新,这可能导致本地缓存和服务器状态不一致的问题。
- 跨设备问题:由于游客账号密码是本地存储的,如果用户更换设备或清理了本地缓存,则需要重新获取一组新的游客账号。这会导致用户体验不一致,并且影响后续的账号合并或正式注册。
- 违反隐私和安全法规:公共接口暴露了获取账号密码的功能,可能会被认为是对用户数据和隐私保护不当,违反一些地区的隐私保护法律(如 GDPR、CCPA 等)。虽然是游客账号,但一旦涉及到用户行为数据或隐私信息,也会带来法律风险。
更合理的解决方案建议:
- 使用服务器分配 Token 方式:
- 动态获取 Token:APP 启动时,可以向服务器请求一个游客 Token,而不是具体的账号密码。这个 Token 可以用来标识该游客账号,并且包含身份验证信息。
- 减少本地存储风险:由于不存储具体的账号密码,降低了因数据库破解带来的账号泄露风险。
- Token 过期机制:设置 Token 的过期时间,避免长期滥用,提升安全性。
- 通过设备标识/UUID 动态生成游客账号:
- 基于设备生成游客账号:可以通过设备的 ANDROID_ID、UUID 或 Google Advertising ID 等设备标识向服务器请求动态生成一个游客账号。
- 持久化存储 Token:APP 只存储分配到的 Token,而不是具体的账号密码,登录时通过 Token 验证身份。
- 统一管理:服务器可以统一管理这些游客账号的状态,避免重复使用、并发冲突等问题。
- 使用 OAuth2.0 或 JWT(JSON Web Token):
- OAuth2.0:用户使用游客模式时,向服务器请求获取一个授权 Token,用于后续 API 请求的身份验证。Token 可以包含权限和失效时间。
- JWT:服务器可以返回 JWT 作为游客身份的凭证,JWT 中可以携带用户的身份信息(如 user_id 和 role),并通过签名防止篡改。JWT 的失效时间可以动态设置,进一步提升安全性。
总结:
从 安全性 和 用户体验 的角度来看,直接在 APP 本地化存储真实的账号密码数据是不合理的。更好的方案是使用 Token 或设备标识进行身份认证,以确保安全性和简化维护工作。
同时,公开、无认证的游客账号获取接口会导致严重的安全问题,如滥用、数据泄露和 DDoS 攻击等。建议在设计时引入认证机制、限流保护和更安全的数据交互方式,以减少潜在的风险并提升系统的安全性。