简单的SQlite DB加密

碰巧我真的很喜欢使用SQLite DBMS。


在汇编程序中编程时,有时我需要一个完整的DBMS。 我的程序很少超过几百千字节。 显然,使用具有数百兆字节的DBMS至少是很荒谬的,但最终非常不便-硬件要求以及安装和配置的复杂性立即增加,结果,整个系统的可靠性降低了。


SQLite是另一回事。 首先,它很小-只有几百千字节,这对紧凑的汇编程序来说是一个很大的补充。 其次,它是一个超可靠的数据存储系统。 她不需要任何特殊设置和设置。 好吧,关于性能-不是最后。


例如,我在AsmBB论坛引擎中使用了SQLite,而我已经在Habré上写过关于SQLite的信息。 (顺便说一句,在那之后他没有摔倒 )。


从那时起,该项目就一直在缓慢但确定地进行中。 出现了新功能,提高了安全性和性能。


然后有一天,我考虑了如何提高该项目本已良好的安全性。 我立即想到加密论坛数据库会很好。 实际上,即使数据库泄漏,也没有人可以访问用户的个人数据。


在Internet上进行的快速搜索显示,有几个用于数据库加密的SQLite扩展。 不幸的是,官方的SEE扩展程序不是免费的,而且通常是为了赚钱而出售的。


但是,当然,一个神圣的地方永远不会是空的,我立即偶然发现了SQLeet扩展。 在其中,我几乎喜欢一切。


SQLeet使用ChaCha20算法来加密数据库。 通过PBKDF2-HMAC-SHA256使用16字节的salt和12345哈希迭代来计算加密密钥。 为了进行身份验证,使用了Poly1305。


SQLeet和SQLite均在公共域(public domain)下分发。 这很方便,因为它不会增加项目中的许可混乱。


SQLeet仍然非常紧凑。 所有代码在C语言中只需要大约一行1.5千行,并且没有外部依赖关系。


该项目得到了积极的支持,作者可以及时回答问题并修复错误(如果有)。


SQLeet的分发方式与SQLite相同-以单个C源文件的形式分发,可以简单地以与SQLite相同的方式对其进行编译。


此外,由于该扩展程序不会以任何方式更改SQLite代码,因此可以非常简单地完成主DBMS的更新-通过替换sqlite3.c文件并重新创建组合的源代码。


由于我在AsmBB中使用的不是标准的编译(AmsBB中的SQLite是通过MUSL libc编译的),而且我不是C程序员,因此对我来说,简化编译非常重要。


例如,这是我用来下载最新版本的SQLeet并创建和构建源代码的bash代码:


 wget -q -O - https://github.com/resilar/sqleet/archive/master.tar.gz | tar -xz cd ./sqleet-master script/amalgamate.sh < ./sqleet.c > ../sqlite3.c cd .. rm -rf ./sqleet-master/ 

结果是一个sqlite3.c文件,该文件可以插入到原始SQLite文件之前插入的地方,并以相同的方式使用。


使用扩展名也与使用SQLite相同。 唯一的区别是,如果数据库已加密,则在打开数据库后立即需要调用sqlite3_key()函数,在该函数中指定加密密码。 好吧,甚至更好,只需执行SQL pragma key='%%' 。 (这更好,因为SQLite API不会更改,并且
您始终可以将SQLeet替换为SQLite,反之亦然)。


初始数据库加密以及密码替换是通过sqlite3_rekey()函数或使用pragma rekey='%NEW_PASSWORD%' pragma进行的。


在这里,我有一个问题。 密码来自哪里? 毕竟,如果密码存储在服务器上的某个文件中,则潜在的黑客将能够读取该密码。


所以我决定以不同的方式做。 事实是AsmBB是一个存在很久的FastCGI应用程序。 在服务器上启动后,它可以运行数月甚至数年,而无需重新启动。


如果是这样,那么管理员只需在启动AsmBB之后立即通过Web界面输入密码即可。 因此,密码仅存在于RAM中,并且仅在应用程序启动期间执行POST请求期间存在。 (当然,不要忘记在执行POST请求期间将存在密码的所有内存清零。)


根据设置的密码,SQLeet通过PBKDF2-HMAC-SHA256生成一个加密密钥,并且该密钥也仅存储在RAM中。


当然,这样的决定是不完善的。 如果攻击者具有管理员权限,则可能在执行AsmBB的过程中在RAM内存中找到加密密钥。


但是即使这样,该系统仍比没有加密的系统安全得多。 例如,现在数据库备份可以存储在任何地方,并可以通过开放渠道发送,而不必担心数据泄漏。


顺便说一句,您可以使用SQLeet(或其他SQLite密码扩展)踩到耙子。 当然,我踩到了它们。


问题是数据库页面的大小。 在低于3.12.0(2016年3月)的SQLite版本中,默认页面大小为1024字节。 在v3.12.0中,创建了4096个字节。 此大小,数据库用户可以出于性能原因而更改,并且页面大小写在数据库本身中。


但是,如果数据库是加密的,则无法读取页面大小,并且由于每个块都是分别加密的,因此需要解密该页面大小。


因此,如果使用非标准页面大小(对于SQLeet,标准为4096字节)加密数据库,那么即使您设置了正确的密码,也将无法对其解密。


这很容易解决-在通过sqlite3_key()pragma key='%%'设置密码之前,您需要通过pragma page_size=%%设置正确的页面大小。


另一个可能的问题是,加密后,操作系统将不再能够识别该文件是SQLite数据库。 据我所知,这有时会导致一些问题,尤其是在iOS中。 有一个解决此问题的方法,只是不对文件的前32个字节进行加密,但是我没有详细介绍。


最后,关于性能。 SQLeet非常快。 加密后,在VPS性能正常波动的背景下,我没有注意到系统中的任何速度下降。 精度测量可能会显示出某种程度的降低,但它可能不到未加密数据库速度的10%之内。


当然,还有其他免费的SQLite扩展可用于加密。 例如, SQLcipher 。 它不适合我,因为它具有不同的分发许可证(BSD),代码更大,并且具有外部依赖关系。


但是,另一方面,SQLcipher较旧,因此(可能)更稳定。 有人可以派上用场。

Source: https://habr.com/ru/post/zh-CN470295/


All Articles