可能大家不太清楚SQLCipher是什么东西?没事,我们慢慢来,首先顺着之前的思路,接着来分析Android端SQLite,上周我们了解了SQLite的使用,那这次我们开篇先来看看SQLite有哪些优缺点?
SQLite作为在Android端频繁使用的轻量型数据库,它的优点不言而喻:易上手、易安装、具备关系型数据库的特征等等,那它有哪些缺陷呢?而这些缺陷又是通过什么技术手段或者方案来解决的呢?
对于一般的APP开发者来说,SQLite易上手的特点会让人很兴奋,但是对于企业级的APP开发来说,性能永远是APP开发的第一指标,而SQLite的最初的设计理念是作为一个轻量级的高性能数据库,大家可能会疑问既然这么设计为什么还会遇到性能问题呢?这里的高性能其实是有条件限制的,在小数据量、并发低、查询结构简单的场景下,在Android端使用SQLite无非是性价比非常高的选择,可是一旦超出了这些限制条件,数据库设计的问题就暴露的很明显了。
对于安全性这个问题是怎么理解的呢?想一想对于每个使用了SQLite的APP来说,它们或多或少都会保存些私密的数据在其中,使用SQLite这对于开发者来说是很方便,但是方便不仅仅是对于他们来说,同时对于一些有“额外想法”的人来说的。免费版本的SQLite有一个致命缺点:不支持加密。意味着,你APP上的数据库其实是在裸奔!对于那些想获取数据的人来说,只要让手机ROOT之后,就可以进入每个APP的目录下面获取db文件从而轻易的得到数据。
性能方面的优化是改造SQLite的最重要一步,优化可以从多个方面切入去提升SQLite的性能。
重点要说的是很多时候我们以为已经建立了索引,但事实上并没有真正生效。这里关键在于如何正确的建立索引。例如使用了BETWEEN、LIKE、OR这些操作符、使用表达式或者CASEWHEN等。
建立索引是有代价的,需要一直维护索引表的更新,比如对于一个很小的表来说就没有必要建索引;如果一个表经常是执行插入更新操作,那么也需要节制的建立索引。总的来说有几个原则:
针对于SQLite的安全问题的解法类似于很多数据库的安全解法,目前常用的方案主要是两类:
对于大部分开发者来说,兼顾安全性和成本的同时,免费版本的SQLCipher也是我们优先采取的安全性加固方案。
上面我们分析了SQLite的优缺点,也基本了解了目前有哪些通用的解决方案,回到我们的主题《攻与防》,这期我们就开始介绍我们的主角-SQLCipher。
基于SQLite接口设计的采用256-bitAES加密算法的安全加密数据库。
我们现在只是了解SQLCipher能够给我们的数据库提供安全加固的保护,那它的实现原理是什么呢?
而SQLCipher就是基于以上的四个接口以及自定义的接口来实现加密的,下面通过图片了解整个加密流程:
上图为SQLCipher的实现原理,加密流程为以下步骤:
PS:以上为默认的算法,SQLCipher没有固定算法,用户可以自己设置。
“最基本的调试方案”,只需要在Linux系统安装好SQLCipher,其他的操作和SQLite无异,唯一需要注意的是SQLCipher是加密库,也就是我们需要给db手动添加密码,操作大致如下:
针对原始的tech_paoding.db数据库生成新的decrypted_database.db数据库
这里的Cipher(也就是加密算法)、KDF、PageSize的值都是默认好的,是SQLCipher3的默认算法的值,如果使用的SQLCipher4的值话,这些数据就需要改变。
对于开发者来说,使用SQLCipher并不会对原本的APP逻辑入侵,只需要按照下面两个步骤即可将SQLite数据库加固。
对于正常使用Android端SQLite来说,引入的SQLite库是android.database.sqlite,比如我们要使用SQLiteDatabase,就需要这样引入:
importnet.sqlcipher.database.SQLiteDatabase;另外需要注意的是,android.database.sqlite是Android项目的内置包,而net.sqlcipher.database.SQLiteDatabase则需要我们自己引入,我的引入方案是从官网拉下对应的aar包,加入到本地的libs中,然后在build.gradle中指定引入路径
implementation(name:'android-database-sqlcipher-3.5.5',ext:'aar')这样,我们就可以顺利的使用SQLCipher进行开发了。
由于SQLCipher的算法是基于SO库开发的,所以我们在正常使用SQLCipher之前需要使用loadLibs方法来加载SQLCipher的SO加密库,例如:
SQLiteDatabase.loadLibs(this);loadLibs方法的逻辑如下:
主要的代码如上图所示,下面看看开发好的Demo的展示:SQLite和SQLCipher的功能已经整合在同一个APP中,通过不同的Activity来展示。
上面我们了解了SQLCipher的由来、原理、基本的使用方法,这一部分我们呼应一下标题《攻与防》,我们结合市面上的用到了SQLCipher加密数据库的APP来看看它们是如何做过安全防护的以及那些“不法者”是怎么去破解这些防护的。
关于百度汉语APP我主要下载了以下几个版本的APK我也发现了他们在不同版本对于整个APP的不同加固方案,这个我们在分析好整个SQLCipher方面的防护之后我们再总结,首先我们使用最新版本APP来分析下,作为词典类型的APP,总会有一个额外内部的数据库来作为离线时的查询方案,那么我们可以寻找下百度汉语APP他们的数据库方案,我们寻找下哪里是他们离线文件的入口,可以很容易在这里看到这里这里有个离线文件下载的功能,于是可以猜测是通过下载离线db文件来填充他们的离线数据库,如第二幅图所示的免费离线包这个列表极可能是通过网络接口动态获取的,因为我们在离线状态下是获取不到的。通过抓这个接口的网络请求包可以发现,可以获取请求db文件的地址根据地址我们获取到了相应的db文件,下载完成后得到这样的文件baidu_dict.db,判断是基于SQLite的文件,放入SQLiteStudio以SQLite方式打开会提示错误,说明应该是基于SQLCipher的加密文件,我们需要寻找的是SQLCipher的秘钥,于是下面我们开始关于秘钥的追踪。
现在我们找到了APP获取秘钥的方法,我们继续跟踪,秘钥采用了SO的函数,调用了libimagerender.so,我们使用IDA具体查看下,直接查看Exports的tab会发现具体的函数属于静态注册的定位到了具体的函数,修复下即可获取真实的秘钥,接着我们使用这个秘钥再去解密数据库即可。
上面分析好了SQLCipher秘钥的获取历程之后,再说说百度汉语APP对于它们的SQLCipher秘钥的加固的演变历程吧,文字就不多说了,直接上图。