跳到主要内容

字段加密适配器

@aiao/rxdb-adapter-encrypted@aiao/rxdb 提供字段级 AES-GCM-256 信封加密,让指定字段在写入数据库文件、变更日志、查询缓存和历史快照时全程保持密文,不留明文痕迹。

加密在适配器层透明完成,上层 Repository API 无需改动。

安装

npm install @aiao/rxdb-adapter-encrypted

peer 依赖任意本地 SQLite 系适配器之一:@aiao/rxdb-adapter-wa-sqlite@aiao/rxdb-adapter-sqlite-wasm@aiao/rxdb-adapter-pglite@aiao/rxdb-adapter-sqliteai。Supabase 等远端适配器不需要此包。

快速开始

1. 标注加密字段

@Property 上加 encrypted: true

import { Entity, Property, PropertyType } from '@aiao/rxdb';

@Entity({ tableName: 'users' })
class User {
@Property({ primaryKey: true }) id!: string;
@Property({ propertyType: PropertyType.string }) displayName!: string;

@Property({ propertyType: PropertyType.string, encrypted: true })
email!: string;

@Property({ propertyType: PropertyType.string, encrypted: true })
phoneNumber!: string;
}

2. 初始化数据库并解锁密钥环

创建 RxDB 实例,注册适配器工厂,连接后调用 adapter.encryption.unlock() 才能读写加密字段:

import { RxDB, SyncType } from '@aiao/rxdb';
import { RxDBAdapterWaSqlite } from '@aiao/rxdb-adapter-wa-sqlite';

const rxdb = new RxDB({
dbName: 'app.db',
context: { userId: 'current-user' },
entities: [User],
sync: {
local: { adapter: 'wa-sqlite' },
type: SyncType.None
}
});

rxdb.adapter('wa-sqlite', async db => new RxDBAdapterWaSqlite(db, { vfs: 'OPFSAdaptiveVFS' }));

await rxdb.connect('wa-sqlite');

const adapter = await rxdb.getAdapter('wa-sqlite');

await adapter.encryption.unlock({
passphrase: 'correct horse battery staple'
});

// 现在可以正常读写加密字段
await rxdb.repository(User).create({
id: 'u1',
displayName: 'Ada',
email: 'ada@example.com' // 写入时自动加密
});

3. 读取时自动解密

const user = await rxdb.repository(User).findOne('u1');
console.log(user.email); // 'ada@example.com',读取时自动解密

解锁方式

unlock() 接受四种互斥的密钥来源:

// 1. 口令(PBKDF2 派生密钥)
await adapter.encryption.unlock({ passphrase: 'my-passphrase' });

// 2. 原始密钥字节(32 字节 Uint8Array)
await adapter.encryption.unlock({ keyBytes: new Uint8Array(32) });

// 3. CryptoKey 对象
await adapter.encryption.unlock({ key: cryptoKey });

// 4. 异步密钥提供者(适合从 Keychain / HSM 获取)
await adapter.encryption.unlock({
keyProvider: async () => fetchKeyFromSecureStorage()
});

空闲自动锁定

默认 5 分钟无操作后自动锁定,可通过 idleTimeoutMs 调整:

await adapter.encryption.unlock({
passphrase: 'my-passphrase',
idleTimeoutMs: 10 * 60_000 // 10 分钟
// idleTimeoutMs: 0 // 禁用自动锁定
});

锁定后读写加密字段会抛 EncryptedLockedError,调用 unlock() 重新解锁即可。

加密规格

项目规格
算法AES-GCM-256
IV每次写入生成唯一 96-bit IV
AADns|table|column|pk|kid(防止跨字段/跨行重放)
信封格式v|alg|kid|iv|ct|tag(6 段 base64url 文本)
存储类型所有加密列在数据库中均以 TEXT 存储
口令派生PBKDF2,验证探针写入 DB,错误口令不保留密钥

约束

以下字段不能标注 encrypted: true

  • 主键(primaryKey: true
  • 外键 / 关联字段
  • 索引字段(index: true
  • 唯一约束字段(unique: true
  • 可排序字段(sortable: true
  • FTS 全文搜索字段(searchable: true
  • 计算字段

违反约束时 RxDB 连接阶段会抛 EncryptedConfigurationError,fail-fast。

对加密字段执行 where / order / group / FTS 查询会抛 EncryptedQueryError(在 SQL 生成前拦截)。

错误类型

错误类触发场景
EncryptedConfigurationErrorschema 违反约束(PK/FK/index 等标注加密)
EncryptedLockedError密钥环未解锁时读写加密字段
EncryptedDecryptError解密失败(数据损坏或密钥不匹配)
EncryptedUnlockError口令错误或密钥验证失败
EncryptedQueryError对加密字段执行过滤/排序/FTS 查询

MVP 范围外

当前版本不包含:

  • 全库加密(仅声明字段被加密)
  • 原生 Keychain / WebAuthn / Passkey 集成
  • 可搜索加密(加密字段不支持查询)
  • 密钥轮换(单 kid
  • 审计日志
  • Tab 可见性变化自动重锁

参考