为什么要做加密服务,最近GDPR对个人数据查的很严,如果违反规定,罚款是很大的,大部分开发的同学是没太多安全意识的,说不定哪天因为系统漏洞导致数据被泄露出去了。
为了避免系统漏洞导致敏感数据被泄露,需要对源头即数据库的数据进行加密,加密之后即使因系统漏洞导致被脱库,泄露的也是加密数据,不是用户的真实数据。
加密范围很大,包括数据库、缓存、其它存储中间件,还有传输过程,为了简化问题,我们今天只讲数据库字段存取的加密。
另外为了简化问题,我们今天所讲方案都是基于以下的用户表设计:
字段 | 类型 | 说明 |
---|---|---|
id | int | 主键 |
name | varchar(50) | 用户名称 |
mobile | varchar(50) | 手机 |
回到正题,如果要设计一个加密服务,大家会怎么设计呢?
任何一个系统的设计都必须要代入业务场景中,我们从用例开始分析具体的业务场景:
用例分2类:
一类是针对业务系统的,每个业务系统的需求就是加密和解密。
数据库字段在其上的操作有:查询,添加、更新、删除,对于加密服务来说,需要加密指定的字段,并且可以对其进行解密;另外针对加密字段还可以查询,这个是一个难点,下面再讲。
对于系统管理员,也就是安全的同学来说需要管理哪些应用在加密,即需要颁发密钥后,应用才可以加密;另外出于安全考虑,还需要定期变更应用密钥。
另一个就是解密的授权管理,即应用A加了密之后,不是所有应用都可以解密的,需要管理加密方和解密方的授权关系。
加密算法选择
因为还要解密,可以选择强度比较高的对称加密算法,如AES256等,这里不专门讲加密算法,有兴趣的同学可以百度下。
数据的存放格式设计
关键的问题来了,加密后的数据怎么存, 直接用加密算法,如AES256加密后存放到数据库字段就行了吗?
实际的场景是加密方可能有多个,即加密的内容,可能是应用A,也可能是应用B加密的,系统上要能区别出来;
另外就是密钥的变更,应用A的密钥会变,不同时期可能是不一样的,一个表的数据可能由多个密钥加密的,即上面的用户表,可能第一行的mobile是由id为1的密钥加密的,而第二行是由id为2的密钥加密的,这2种情况都需要能被解密出来 。
所以加密字段不仅要存放加密后的内容,还要存放一些元信息:哪个应用加密的,哪个密钥加密的。
相关表设计
应用表 | ||
---|---|---|
字段 | 类型 | 说明 |
id | int | 主键 |
name | varchar(50) | 应用名称 |
应用密钥表 | ||
字段 | 类型 | 说明 |
id | int | 自增主键 |
app_id | int | 应用id |
key | varchar(64) | 密钥 |
version | varchar(4) | 版本号 |
algrithom | varchar(64) | 加密算法 |
加密内容格式,我们建议要包含上面信息,即可以把应用密钥表的id和加密后的内容存放一起,如:密钥id + ^ + 加密内容。
解密时,先解析得到密钥表的id和加密内容,根据id得到密钥,然后进行解密,这样每次变更密钥之后,历史数据不受影响。
加密字段查询
一个字段加密之后,还要根据其查询,这样的场景应该还是不少的,像用户表的手机号。
我们首先说下问题在哪里,在某个时间密钥换了,即user表的mobile可能被N个密钥加密,即总共2行数据,可能第一行是用密钥A加密的,第二行却是用密钥B加密的,先将原文加密去查询的话,用哪个密钥呢?
这里的问题是,由一个输入得不到一个确定的输出,所以我们的问题就是如何由一个确定的输入在任何时间得到不变的输出。
1、等值查询
最简单的方案就是加字段,即在user表再加个mobile_hash字段,这里存放将mobile字段hash后的值,hash算法可以选md5或sha256,这样在任何时候由输入可以得到一个确定的输出;但这里有个风险,md5算法可能碰撞,根据HASH查询出来之后可能不是输入的手机号,业务上需要解密下是不是和原文相等。
当然也可以不加字段,可以将hash后的值存放到mobile的前面,每次查询用like进行左前缀匹配。
2、需要做like查询
这里方案比较复杂,也比较烧脑,有一篇论文讲的比较详细,有兴趣的同学可以看下:https://www.jiamisoft.com/blog/5961-kuaisuchaxunshujukujiami.html 。