29、安全与隐私:数据加密存储、用户授权撤销、GDPR合规检查清单
说实话,做位置相关的开发,最让我睡不着觉的,不是定位不准,而是数据泄露。
我早年接手过一个项目,用户位置数据直接明文存SQLite,连个加密都没有。后来被安全团队扫出来,差点被要求全量下架整改。从那以后,我对位置数据的加密和隐私合规,就特别较真。
这一章,咱们就把安全与隐私这块硬骨头啃下来。我会把我在项目中踩过的坑、用过的方案,以及GDPR合规的检查清单,一次性讲清楚。
29.1 数据加密存储:别让用户的位置“裸奔”
位置数据一旦落地,就必须加密。这不是选择题,是必答题。
我个人习惯把加密分成两个层面:传输加密和存储加密。传输加密靠HTTPS,这个基本是标配了。存储加密才是今天要聊的重点。
29.1.1 本地数据库加密:用SQLCipher还是Room + 加密?
Android端最常用的本地数据库是SQLite。但SQLite本身不加密。你想想看,如果用户手机丢了,或者被root了,别人直接把db文件拷走,用个SQLite浏览器就能看到所有位置记录——这太可怕了。
我推荐两种方案:
- 方案一:SQLCipher。这是SQLite的加密扩展,对上层API几乎透明。你只需要在打开数据库时传入一个密钥,它自动帮你完成加密和解密。我在一个物流App里用过,性能损耗大概在5%-10%,完全可以接受。
- 方案二:Room + EncryptedSharedPreferences。如果你用的是Room,可以配合SQLCipher的Room支持库。对于小量敏感数据(比如最近一次定位的经纬度),用EncryptedSharedPreferences更轻量。
核心原则:密钥不要硬编码在代码里。一定要用Android Keystore系统来生成和存储密钥。我曾经见过有人把密钥写在strings.xml里……嗯,那跟没加密没啥区别。
// 使用SQLCipher + Room的示例
// 注意:密钥应从Keystore获取,这里仅为演示
public class LocationDatabase extends RoomDatabase {
private static final String DB_NAME = "location_db";
private static final byte[] PASSPHRASE = "your_secure_key_here".getBytes();
public static LocationDatabase getInstance(Context context) {
// 使用SQLCipher的SupportFactory
final SupportFactory factory = new SupportFactory(PASSPHRASE);
return Room.databaseBuilder(context, LocationDatabase.class, DB_NAME)
.openHelperFactory(factory)
.build();
}
}
29.1.2 文件级加密:位置轨迹文件的保护
如果你的App需要导出位置轨迹文件(比如GPX格式),那这些文件也要加密。我一般用AES-GCM模式,因为GCM模式自带认证,能防止数据被篡改。
我的小技巧:对于大文件,不要一次性加载到内存再加密。用流式加密,边读边写,避免OOM。我曾经在导出一天轨迹时踩过这个坑,文件有几十MB,直接内存爆了。
29.2 用户授权撤销:给用户“反悔”的权利
GDPR和国内的《个人信息保护法》都明确要求:用户有权撤回同意。说白了,用户今天允许你定位,明天就可以不让。
但很多App只做了授权请求,没做授权撤销。用户一旦给了权限,就再也收不回来了——这其实是不合规的。
29.2.1 运行时权限的撤销处理
Android 6.0以上,用户可以在设置里手动撤销权限。但你的App需要能感知到这个变化。我建议在onResume()里检查权限状态,如果发现权限被撤销,要立即停止定位并清理缓存数据。
@Override
protected void onResume() {
super.onResume();
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// 权限被撤销了!立即停止定位
stopLocationUpdates();
// 清理本地缓存的位置数据
clearCachedLocations();
// 通知用户
showPermissionRevokedDialog();
}
}
29.2.2 应用内的“一键撤销”功能
除了系统设置,你的App内部也应该提供一个“撤销授权”的入口。我习惯在设置页面放一个开关,用户关掉后,不仅停止定位,还要删除所有已收集但未上传的位置数据。
注意:撤销授权不等于删除数据。如果数据已经上传到服务器,你还需要提供“删除账户数据”的入口。这个在GDPR里叫“被遗忘权”。
29.3 GDPR合规检查清单:一条一条过
GDPR(通用数据保护条例)虽然是个欧洲法规,但只要你面向欧盟用户提供服务,就必须遵守。我整理了一份检查清单,每次做位置相关项目时,我都会拿着它一条一条过。
| 检查项 | 具体要求 | 我的备注 |
|---|---|---|
| 1. 明确告知 | 在收集位置前,用清晰的语言告知用户:收集什么数据、为什么收集、怎么用 | 不要用小字藏在隐私政策里。我习惯在首次定位时弹出一个独立的说明弹窗 |
| 2. 合法依据 | 必须有合法的处理依据,通常是“用户同意”或“合同必要” | 如果App的核心功能不需要位置,就不能把位置授权作为使用条件 |
| 3. 数据最小化 | 只收集实现功能所必需的最少数据 | 比如只需要粗略位置,就别申请精确定位权限 |
| 4. 存储限制 | 数据保存时间不能超过必要期限 | 我一般设置30天自动清理,除非用户明确要求保留 |
| 5. 用户权利 | 支持访问、更正、删除、撤回同意、数据可携带 | 数据可携带的意思是:用户要求时,你要能导出他的位置数据给他 |
| 6. 数据安全 | 采取适当的技术措施保护数据 | 加密存储、传输加密、访问控制,一个都不能少 |
| 7. 跨境传输 | 如果数据传到欧盟以外,要有合法机制 | 比如标准合同条款(SCC)或充分性认定 |
| 8. 数据保护影响评估 | 对高风险处理活动进行DPIA | 位置数据属于高风险类别,建议做DPIA |
一句话总结:GDPR的核心就四个字——尊重用户。你想想看,如果你自己的位置数据被App偷偷收集、明文存储、无限期保留,你是什么感受?
29.4 安全与隐私知识体系图
下面这张图,我把本章的核心知识点串起来了。你可以把它当作一个快速参考框架。
29.5 避坑指南:我踩过的那些坑
最后,分享几个我亲身经历过的教训,希望能帮你少走弯路。
- 坑一:密钥硬编码。我曾经在一个项目里,把数据库加密密钥写在了BuildConfig里。结果反编译APK后,密钥直接被看到了。后来我改用Keystore + 生物识别解锁,才算真正安全。
- 坑二:权限撤销后还在后台定位。有个版本,用户明明在设置里关了定位权限,但我们的Service还在跑,因为没检查权限状态。结果被用户投诉到应用商店,说我们“偷偷定位”。从那以后,我每次启动定位前都会检查权限。
- 坑三:GDPR的数据可携带性没做。有个欧盟用户要求导出他的位置数据,我们才发现根本没有导出功能。最后只能让后端手动从数据库里查出来,再拼成CSV发邮件——又慢又尴尬。
我的建议:安全与隐私不是上线前才考虑的。从架构设计的第一天,就要把加密、授权撤销、合规检查融入进去。否则后期改起来,成本翻倍还不止。
好了,这一章的内容就到这里。位置数据的安全与隐私,说白了就是三个词:加密、尊重、合规。把这三点做到位,你的App才能让用户放心用,让监管挑不出毛病。
公众号:蓝海资料掘金营,微信deep3321