Skip to content

安全与隐私设计

爱生活 采用三层纵深安全防护架构,从应用入口到数据存储逐层加固,确保用户数据仅且只能由用户本人访问。

1. 安全架构总览

┌─────────────────────────────────────────────────────────────┐
│                  第一层:应用入口防护                          │
│  隐私协议同意  →  屏幕截图/录屏保护  →  后台自动锁屏           │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                  第二层:身份认证层                            │
│  加密密码 / PIN 码  ←→  生物认证 (Face ID / Touch ID / 指纹) │
│  自定义安全键盘输入  │  5 档自动锁定时间                      │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                  第三层:数据加密层                            │
│  AES-256-GCM (原生) / AES-256-CBC + HMAC-SHA256 (降级)      │
│  PBKDF2 密钥派生 (SHA-256, 10,000 次迭代)                   │
│  数据库透明加密 (EncryptedStore)                              │
└─────────────────────────────────────────────────────────────┘

设计原则

  • 零知识架构:加密密钥仅存于用户脑中(密码)和设备安全硬件(生物认证),服务端无法解密
  • 透明加密:业务代码零感知,EncryptedStore 自动完成序列化 → 加密 → 存储 → 解密全流程
  • 纵深防御:任一单层被突破,后续层级仍可保护数据安全
  • 本地优先:全部数据存储在本地,云端同步采用端到端加密

2. 加密体系

/src/utils/crypto.ts — 核心加密模块,提供跨端统一的加解密能力。

2.1 密钥派生

用户密码通过 PBKDF2 派生为 256 位 AES 密钥:

参数说明
算法PBKDF2-HMAC-SHA256业界标准密钥派生函数
迭代次数10,000移动端约 50ms,兼顾安全与体验
密钥长度256 bit (32 bytes)AES-256 所需长度
Salt 长度256 bit (32 bytes)随机生成,存储于加密配置中

每次设置密码时随机生成新的 Salt,同一密码在不同设备上产生不同密钥。

2.2 加密算法

系统在两种环境下自动选择最优算法:

环境加密算法认证方式IV 长度
原生环境(有 crypto.subtleAES-256-GCMGCM 内置认证标签12 bytes
降级环境(小程序等)AES-256-CBCHMAC-SHA25616 bytes

AES-256-GCM(首选):提供认证加密(AEAD),同时保证机密性和完整性,是目前最安全的对称加密模式之一。

AES-256-CBC + HMAC-SHA256(降级):当原生 crypto.subtle 不可用时(如部分小程序环境),使用 crypto-js 库实现。加密后额外计算 HMAC 校验值防止篡改。

2.3 随机数生成

  • 优先crypto.getRandomValues() — 密码学安全的伪随机数生成器
  • 降级Math.random() — 仅在不支持 CSPRNG 的极端环境下使用

2.4 加密/解密流程

加密:plaintext → JSON.stringify → crypto.encrypt(key, iv) → { _enc, _iv, _algo, _mac? }
解密:{ _enc, _iv, _algo, _mac? } → crypto.decrypt(key, iv) → JSON.parse → plaintext

加密后的密文包含完整的元信息(IV、算法标识、可选的 HMAC),解密时无需额外配置。

3. 透明加密存储

/src/database/engines/encrypted-store.ts — 数据库透明加密封装层,对上层业务完全透明。

3.1 设计理念

业务代码调用 store.put(data)

    EncryptedStore 拦截

    JSON.stringify(data) → AES 加密 → 写入底层存储

    底层存储中仅存 { id, _enc, _iv, _algo, _mac? }

业务代码调用 store.get(id)

    EncryptedStore 拦截

    从底层读取 → AES 解密 → JSON.parse → 返回原始对象

业务层完全无感知,Repository 不包含任何加密逻辑。

3.2 加密记录格式 (EncRecord)

每条数据库记录在磁盘上的格式:

字段类型说明
idstring主键,保持明文以支持索引查询
_encstring密文(Base64 编码)
_ivstring初始化向量(Base64 编码)
_algostring算法标识:gcmcbc
_macstring?HMAC 校验值(仅 CBC 模式,可选)

id 保持明文是有意设计:支持 where('id', value) 的索引快速查找,同时 id 是 UUID,不包含敏感信息。

3.3 EncryptedStore 接口

核心类实现 IStore<T> 接口:

方法说明
put(item)加密后写入单条记录
get(id)读取并解密单条记录
bulkPut(items)批量加密写入
toArray()全量读取并解密
delete(id)删除记录

3.4 EncryptedCollection 查询

由于数据已加密,非 id 字段的查询需要特殊处理:

where(field, value)
    ↓ 非 id 字段?
    → 全量读取 → 逐条解密 → 内存过滤
方法说明
where('id', value)利用明文 id 索引,O(1) 直接命中
where(otherField, value)全量解密后内存过滤
filter(fn)自定义过滤函数,同样在解密后执行
limit(n)限制返回条数
sortBy(field)按字段排序
reverse()反转结果集

容错机制:批量解密时使用 Promise.allSettled,单条记录解密失败不影响其他记录的正常返回。

3.5 平台自适应存储工厂

/src/database/storage-store.ts 根据运行环境自动选择底层存储引擎:

平台底层引擎加密包装
H5 (Web)Dexie (IndexedDB)EncryptedStore
App (iOS/Android)SQLite (原生)EncryptedStore
typescript
function createStore<T extends { id: string }>(tableName: string): IStore<T> {
  const inner = createInnerStore(tableName)  // 平台选择
  return new EncryptedStore<T>(inner)        // 透明加密
}

4. 密钥管理

4.1 密钥生命周期

设置密码 → PBKDF2 派生密钥 → 存入内存 (ActiveKey)

         用户解锁 → 输入密码 → PBKDF2 → 比较验证令牌 → 设置 ActiveKey

         自动锁定 / 手动锁定 → 清除内存 ActiveKey

         生物认证 → Keychain/Keystore 读取密码 → PBKDF2 → 恢复 ActiveKey

4.2 内存密钥

/src/utils/crypto.ts — 活跃密钥仅存储于内存中:

操作说明
setActiveKey(key)将派生密钥写入内存(模块级变量)
getActiveKey()获取当前活跃密钥,不存在返回 null
锁定/退出内存释放,密钥从 RAM 中清除

密钥不写入磁盘、不序列化、不进入 Swap — 仅存在于进程内存中。

4.3 平台安全存储

生物认证场景下,加密密码需要安全持久化:

平台安全存储API
iOSKeychain ServicesSecItemAdd / SecItemCopyMatching
AndroidAndroid KeystoreKeyStore + KeyChain

存储策略:kSecAttrAccessibleWhenUnlockedThisDeviceOnly — 仅在设备解锁后可访问,且不可迁移到其他设备。

4.4 加密配置持久化

加密配置通过 uni.storage + plus.storage 双写持久化(确保 iOS/Android 可靠),存储键 lifeos_enc_cfg

字段说明
salt32 字节随机 Salt(Base64)
verifyToken验证令牌密文(明文为 LIFEOS_VERIFY_OK
verifyIV验证令牌 IV
verifyAlgo验证令牌加密算法
verifyMAC验证令牌 HMAC(仅 CBC 模式)

验证令牌的作用:无需解密真实数据即可验证密码正确性。输入密码 → 派生密钥 → 解密验证令牌 → 等于 LIFEOS_VERIFY_OK 则密码正确。

4.5 加密管理器

/src/utils/encryption-manager.ts — 加密配置的生命周期管理:

函数说明
setupEncryption(password)首次设置密码,生成 Salt 和验证令牌
verifyEncPassword(password)验证密码(纯验证,不设 ActiveKey)
unlock(password)验证密码并设置 ActiveKey
lock()清除 ActiveKey
changePassword(oldPwd, newPwd)修改密码(重新派生密钥,更新验证令牌)
isEncryptionConfigured()检查是否已完成加密设置

5. 应用锁屏

/src/utils/lock-manager.ts — 独立于加密模块的锁屏管理器,提供应用级访问控制。

5.1 锁屏模式

模式配置键说明
enc加密密码与数据加密使用同一密码,解锁同时恢复 ActiveKey
pin独立 PIN 码仅作锁屏验证,不解密数据(适用于数据未加密场景)

PIN 码通过简单哈希存储(带固定 Salt lifeos_lock_salt_),生成 pin_xxx 格式的哈希值。注意:PIN 模式仅提供浅层访问控制,不参与数据加密。

5.2 自动锁定

支持 5 档自动锁定时间:

选项时间
1 分钟最高安全级别
5 分钟高安全级别
15 分钟推荐默认值
30 分钟中等安全
60 分钟较低安全(不建议)

App 进入后台时立即触发倒计时,超时后自动清除内存密钥并锁定。

5.3 锁屏状态管理

/src/utils/lock-state.ts — 全局锁屏状态:

typescript
appLocked: Ref<boolean>     // 当前是否锁定
appForeground: Ref<boolean>  // App 是否在前台
函数说明
forceLock()强制锁定(清除密钥 + 导航到锁屏页)
forceUnlock()解锁并重新加载所有 Store
checkAndLock()加密已配置但无活跃密钥时自动锁定

6. 生物认证

/src/utils/biometric-manager.ts + /src/components/common/BioAuthOverlay.vue — 利用系统级生物认证提供便捷且安全的解锁体验。

6.1 认证流程

用户面对 BioAuthOverlay

prompt 状态:自动触发系统生物认证

    ┌─ 认证成功 → Keychain 读取密码 → PBKDF2 派生密钥 → 解锁
    └─ 认证失败 → retry 状态:提供再次尝试 / 密码解锁选项

              密码解锁 → 自定义安全键盘输入 → 验证密码 → 解锁

6.2 安全键盘

BioAuthOverlay.vue 使用自定义键盘而非系统键盘:

  • QWERTY / 数字 / 符号三种布局切换
  • 密码显示为 •••• 掩码,最长 64 字符
  • 不唤起系统键盘,防止第三方键盘记录
  • 错误时抖动动画提示

6.3 生物认证管理器

函数说明
isBiometricAvailable()检测设备是否支持生物认证
enableBiometricUnlock(password)将密码写入 Keychain/Keystore
disableBiometricUnlock()从 Keychain/Keystore 删除密码
authenticateWithBiometric()触发系统生物认证

存储标识:

字段
Servicecom.lifeos.biometric
Accountenc_password

6.4 iOS 原生实现

BiometricAuth.swift — 使用 LAContext + .deviceOwnerAuthentication 策略:

  • 支持 Face ID / Touch ID / 设备密码回退
  • Keychain 操作通过 Security 框架
  • 提供 dumpKeychain() 诊断接口

H5 和小程序环境 isBiometricAvailable() 始终返回 false

7. 屏幕安全

/src/uni_modules/screen-security/ — 防止敏感信息通过截图或录屏泄露。

7.1 iOS 截图保护

ScreenSecurity.swift — 利用 UITextField.isSecureTextEntry 特性:

在 window layer 中插入隐藏的安全文本输入层
    → iOS 系统级截图/录屏保护自动生效
    → 多任务切换器中也无法截取应用内容

这是 Apple 推荐的防止敏感内容被截图的方案,利用系统行为而非 Hook,稳定且合规。

8. 数据管理

/src/pages/data-management/index.vue — 提供完整的数据生命周期管理。

8.1 数据导出

导出备份
    → rawStore.toArray()(读取加密密文,不解密)
    → 打包为 JSON 文件
    → 分享 / 保存到本地

备份文件包含加密密文,即使备份文件泄露,没有用户密码也无法解密。

8.2 数据导入

导入备份
    → 选择 JSON 备份文件
    → 覆盖写入所有 Store(需当前已有活跃密钥)

导入时使用当前密钥重新加密,确保数据一致性。

8.3 密码遗忘恢复

针对"忘记密码但仍有备份"的场景,提供特殊的合并流程:

合并旧数据
    → 输入旧密码
    → 从旧备份中提取 Salt → PBKDF2 派生旧密钥
    → 解密旧备份数据
    → 用当前活跃密钥重新加密
    → 写入当前数据库
    → 自动清除旧备份文件

这是唯一能恢复因忘记密码而无法访问数据的途径 —— 前提是用户之前做过导出备份。

8.4 数据清除

清除全部数据
    → 二次确认(危险操作警告)
    → 清空所有 Store
    → 可选择是否同时重置加密配置

9. 隐私合规

9.1 隐私协议

/src/pages/privacy/agree.vue — 首次启动强制展示,不同意则无法使用:

  • 同意 → 存储 privacy_agreed = '1',进入引导页
  • 不同意 → 退出应用(plus.runtime.quit() / window.close()
  • 可随时点击查看完整隐私政策

9.2 隐私政策内容

/src/pages/privacy/index.vue — 完整隐私政策涵盖:

章节内容
数据存储本地为主,AES-256 加密,服务端无法解密
权限说明存储、网络、位置、相机、麦克风、通知、振动、生物认证、蓝牙
第三方 SDK高德地图 SDK、百度语音 SDK、SQLite
用户权利查看权、导出权、删除权、撤回同意权
未成年人保护不主动收集未成年人信息
政策更新重大变更时应用内通知

9.3 权限最小化

权限用途按需申请
存储数据持久化、备份导出启动时
网络云端同步、AI 对话使用时
位置轨迹记录用户主动触发
相机拍照记录用户主动触发
麦克风语音输入用户主动触发
通知任务/习惯提醒设置时
生物认证快速解锁用户主动开启

所有敏感权限均采用按需申请策略,不预授权。

10. 启动安全流程

/src/App.vue — 应用启动时执行完整的安全检查链:

App 启动

① privacy_agreed? ──→ 否 → 跳转隐私协议页(无法跳过)
    ↓ 是
② onboarding_complete? ──→ 否 → 跳转引导页
    ↓ 是
③ isEncryptionConfigured? ──→ 否 → 跳转加密设置页
    ↓ 是
④ isLockConfigured? ──→ 否 → 跳转锁屏设置页
    ↓ 是
⑤ getActiveKey()? ──→ 无 → 应用锁定,显示解锁弹窗
    ↓ 有
正常使用

后台锁屏:App 进入后台(pause 事件)→ 自动清除活跃密钥 → 设置锁定状态。回到前台时需通过锁屏验证。

11. 安全威胁模型

11.1 防御的威胁

威胁防御机制
设备丢失/被盗锁屏密码 + 自动锁屏 + AES-256 加密
恶意应用读取数据应用沙盒 + 数据库加密
网络传输窃听端到端加密(服务端无法解密数据)
截图/录屏泄露iOS 截图保护
暴力破解密码PBKDF2 10,000 次迭代(每次约 50ms)
键盘记录自定义安全键盘
备份文件泄露备份为加密密文

11.2 不防御的威胁

威胁原因用户建议
设备已越狱/Root系统安全边界已被打破避免在已越狱设备上使用
用户密码过于简单加密强度取决于密码强度使用 12 位以上混合密码
内存转储攻击密钥在内存中明文存在使用后及时锁定
社工/钓鱼人为因素不向任何人透露密码

11.3 安全边界

┌────────────────────────────────────────┐
│              用户密码                    │
│              (信任根)                    │
├────────────────────────────────────────┤
│           PBKDF2 密钥派生               │
├────────────────────────────────────────┤
│          内存中的 ActiveKey              │
│           ← 安全边界(进程内存)          │
├────────────────────────────────────────┤
│          透明加密存储                    │
├────────────────────────────────────────┤
│          平台安全硬件                    │
│   iOS Keychain / Android Keystore      │
└────────────────────────────────────────┘

信任根是用户的密码。密码强度直接决定整体安全性。系统建议用户在加密设置页使用随机密码生成器(Star$Moon#1234 格式),并在安全的地方备份密码。