加密SQLite

时间:2011-12-05 11:45:27

标签: sqlite

我将编写自己的加密,但想讨论一些内部。应该在几个移动平台上使用 - iOS,Android,WP7,桌面服务或多或少作为测试平台。

让我们首先介绍现有解决方案的简要特征:

  • SQLite标准(商业)SEE扩展 - 我不知道它如何在内部工作以及它如何与上述移动平台合作。

  • System.data.sqlite(仅限Windows):完整DB,ECB模式的RC4加密。它们也加密了DB头,偶尔会有(0.01%几率)导致数据库损坏。*)其他优点:他们使用SQLite合并分发。

  • SqlCipher(openssl,即几个平台):可选择的加密方案。他们加密整个数据库。 CBC模式(我认为),随机IV矢量。因此,他们必须修改页面参数(大小+保留空间以存储IV)。他们意识到与未加密读取数据库标头有关的问题,并尝试引入变通方法,但解决方案并不令人满意。另外一个缺点:他们使用SQLite3源代码树。 (另一方面 - 其他功能 - 启用其他功能,即使用特殊编译指示对加密参数进行微调。)

根据我自己的分析,我认为以下可能是一个不会遇到上述问题的好解决方案:

  • 加密除DB标头之外的整个数据库。
  • ECB模式:听起来有风险,但在简要查看数据库格式之后,我无法想象如何利用这种攻击进行攻击。
  • AES128?
  • 在SQLite合并之上实现(类似于system.data.sqlite)

我想讨论这种加密方案的可能问题。

*)由于SQLite读取DB头而没有解密。由于RC4(流密码),这个问题将在第一次使用时出现。 AES会更加危险,因为每个“实时”数据库迟早都会面临这个问题。


已编辑 - 基于VFS的加密案例

上述方法使用sqlite.org认可的基于编解码器的方法。它是一组3个回调,最重要的是这个:

void *(*xCodec)(void *iCtx, void *data, Pgno pgno, int mode)

此回调在SQLite自行决定时用于加密/解密从磁盘读取/写入的数据。数据逐页交换。 (Page是512 By的倍数。)

替代选项是使用VFS。 VFS是一组用于低级OS服务的回调。其中有几个与文件相关的服务,例如XOPEN / xSeek / xRead / xWrite / xClose。特别是,这里是用于数据交换的方法

int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);

这些调用中的数据大小范围从4 By(常见情况)到DB页面大小。如果你想使用块密码(还有什么用?),那么你需要组织底层块缓存。我无法想象一个与SQLite内置事务一样安全且高效的实现。

第二个问题:VFS实现依赖于平台。 Android / iOS / WP7 /桌面都使用不同的来源,即基于VFS的加密必须逐个平台地实施。

下一个问题是一个更微妙的问题:平台可能会使用VFS调用来实现文件锁定。这些用途不得加密。此外,不得缓冲共享锁。换句话说,VFS级别的加密可能会危及锁定功能。


EDITED - 对基于VFS的加密的纯文本攻击

我后来意识到这一点:DB标头以固定字符串“SQLite format 3”开头,标头包含许多其他固定字节值。这为已知的明文攻击(KPA)打开了大门。

这主要是基于VFS的加密问题,因为它没有加密数据库标头的信息。

System.data.sqlite也有这个问题,因为它加密(RC4)也是DB头。

SqlCipher用盐覆盖hdr字符串,用于将密码转换为密钥。此外,它默认使用AES,因此KPA攻击没有任何危险。

2 个答案:

答案 0 :(得分:4)

您不需要破解db格式或sqlite源代码。 SQLite公开了虚拟文件系统(vfs)API,它可以用来包装文件系统(或其他vfs),加密层可以动态加密/解密页面。当我这样做时,结果是非常简单的任务,只需几百行代码。这样整个数据库将被加密,包括日志文件,它对任何客户端代码都是完全透明的。典型的页面大小为1024,几乎可以使用任何已知的分组密码。从我的文档中可以得出结论,这正是SQLCipher所做的。

关于“问题”,您会看到:

  • 您不需要重新实现文件系统支持,您可以包装默认的VFS。所以没有锁或平台依赖的问题。
  • SQLite的默认操作系统后端也是VFS,除了你添加之外没有使用VFS的开销。
  • 您不需要块缓存。当然,当它要求只有4个字节时你必须读取整个块,但是不要缓存它,它将永远不会被再次读取。 SQLite有自己的缓存来阻止它(寻呼机模块)。

答案 1 :(得分:1)

没有得到太多回应,所以这是我的决定:

  • 自己加密(AES128),CBC模式

  • 编解码器接口(与SqlCipher或system.data.sqlite使用的相同)

  • 数据库标题未加密

  • 页面标题也未加密并用于IV生成

  • 使用合并SQLite分发

AFAIK此解决方案应该比SqlCipher或system.data.sqlite更好。