数据存储安全:SharedPreferences加密、SQLite数据库加密、文件存储权限控制、KeyStore使用

数据存储,是每个Android应用都绕不开的环节。说白了,你的用户信息、登录状态、缓存数据,最终都得落地到设备上。但落地不等于安全——我见过太多应用,直接把密码明文存到SharedPreferences里,或者把数据库文件暴露给第三方。嗯,今天我们就来聊聊,怎么把这些存储手段武装起来。

SharedPreferences加密:别让明文成为习惯

SharedPreferences用起来确实方便,但它的存储路径是/data/data/包名/shared_prefs/。只要设备root了,或者有备份漏洞,这些XML文件就能被直接读取。我个人习惯是:敏感信息绝不直接存

核心原则:SharedPreferences只存非敏感标记位,比如“是否首次启动”。登录token、用户手机号这些,必须加密后再存。

怎么做?我推荐用AES加密,把值加密成Base64字符串再写入。读取时先解密。你可以自己封装一个工具类,或者直接用EncryptedSharedPreferences——这是AndroidX Security库提供的官方方案。

// 使用 EncryptedSharedPreferences 示例
MasterKey masterKey = new MasterKey.Builder(context)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .build();

SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
        context,
        "secure_prefs",
        masterKey,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

sharedPreferences.edit().putString("token", "your_token_value").apply();

你看,代码量没多多少,但安全性提升了一个档次。我在项目中遇到过,有人直接用Base64编码当加密——这其实只是编码,不是加密,解码毫无难度。避坑指南:加密一定要用正规算法,别自己发明

SQLite数据库加密:用SQLCipher给数据加把锁

SQLite是Android内置的轻量数据库,但默认不加密。数据库文件存放在/data/data/包名/databases/下,root设备上随便拉出来就能用SQLite浏览器打开。你想想看,用户聊天记录、订单信息全在里面,多危险。

我的做法是:用SQLCipher替换原生SQLite。SQLCipher是SQLite的加密扩展,支持256位AES加密。集成方式也很简单,把依赖加进去,数据库打开时传入密码即可。

// 集成 SQLCipher 示例
implementation 'net.zetetic:android-database-sqlcipher:4.5.6'

// 打开加密数据库
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(
        databaseFile,
        "your_password",
        null
);

小提示:密码不要硬编码在代码里。可以从KeyStore中获取,或者由用户输入(比如应用锁密码)。我曾经见过把密码写在BuildConfig里的,反编译一下就暴露了。

另外,如果你用的是Room,SQLCipher也支持无缝集成。只需要在Room.databaseBuilder()里调用.openHelperFactory()传入SupportFactory即可。

文件存储权限控制:内外有别

Android的文件存储分为内部存储和外部存储。内部存储是每个应用私有的,其他应用默认无法访问。外部存储则分公有目录和私有目录。嗯,这里有个常见的坑:很多人把敏感文件直接丢到外部存储的公有目录,比如DCIMDownload

我个人建议:

  • 内部存储:存放数据库、SharedPreferences、应用私有配置文件。权限天然隔离,不需要额外处理。
  • 外部存储私有目录getExternalFilesDir()获取的目录,适合存放用户生成的大文件,比如图片、视频。其他应用无法直接访问,但备份工具可能读到。
  • 外部存储公有目录:尽量只放非敏感文件,比如分享出去的图片。敏感数据不要放这里。

警告:从Android 10开始,WRITE_EXTERNAL_STORAGE权限对私有目录不再生效。但如果你还在用targetSdkVersion低于29,记得检查权限适配。

还有一个细节:文件权限设置。内部存储的文件默认是MODE_PRIVATE,但如果你用了MODE_WORLD_READABLEMODE_WORLD_WRITEABLE(API 17已废弃),那就等于把门打开了。我在项目中排查过一个漏洞,就是有人为了跨进程共享文件,用了MODE_WORLD_READABLE,结果所有应用都能读。

KeyStore使用:把密钥锁进硬件里

前面提到的加密,密钥放哪?你总不能把密钥和密文放一起吧。Android KeyStore就是干这个的——它把密钥存储在系统级的安全容器中,甚至支持硬件级安全(TEE、StrongBox)。

说白了,KeyStore就是一个密钥保险箱。应用只能通过KeyStore的API使用密钥,但无法直接导出密钥原文。这就避免了密钥被反编译或内存dump的风险。

// 生成并存储密钥到 KeyStore
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);

KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
        "my_key_alias",
        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
)
        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
        .setKeySize(256)
        .build();

KeyGenerator keyGenerator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES,
        "AndroidKeyStore"
);
keyGenerator.init(spec);
keyGenerator.generateKey();

使用时,通过别名从KeyStore中取出密钥,然后进行加解密操作。注意:KeyStore在Android 4.3以下不支持,但现在的设备基本都兼容了。

避坑指南:我曾经遇到一个问题——KeyStore生成的密钥在应用卸载后会被清除。如果你需要跨版本升级保留密钥,记得做备份方案。但备份本身也要安全,不能简单存到文件里。

知识体系总览

下面这张图,帮你理清数据存储安全的四个核心模块和它们之间的关系:

Android 数据存储安全知识体系 SharedPreferences 加密存储 EncryptedSharedPreferences SQLite数据库 加密存储 SQLCipher 文件存储 权限控制 内部/外部存储 KeyStore 密钥管理 硬件级安全 核心安全策略 1. 敏感数据加密存储,不存明文 2. 数据库使用SQLCipher加密 3. 文件存储遵循内外有别原则 4. 密钥统一托管在KeyStore中

这四个模块不是孤立的。比如你用KeyStore存储SQLCipher的数据库密码,用EncryptedSharedPreferences存储用户登录状态,用内部存储存放日志文件——它们组合起来,才能构建一个完整的数据安全防线。

我的经验:安全开发不是一次性工作。每次发布版本前,我都会做一次数据存储审计:检查所有SharedPreferences的key-value、数据库表结构、文件读写路径。养成习惯后,很多漏洞在开发阶段就被扼杀了。

好了,数据存储安全这块,核心就是四个字:加密、隔离。加密让数据不可读,隔离让数据不可达。做到这两点,你的应用在存储层面就基本合格了。

公众号:蓝海资料掘金营,微信deep3321