免费注册,打造高效身份管理
博客/案例/被脱库咋办?KMS 给你解决方案!
被脱库咋办?KMS 给你解决方案!
Authing 官方2020.06.18阅读 676

最近经常能听见某某某公司用户信息被泄漏了,某某某公司用户开房记录被脱裤了 ... 数据隐私是企业的生命线,其重要性我想无需我多说什么。

之前我们科普了 AWS KMS 服务:

Authing:AWS KMS 科普: What Why and How?zhuanlan.zhihu.com图标

简要回顾一下:KMS 全称为 Key Management Service,也就是一个密钥管理系统,它在设计上从根本解决了数据的安全性问题,同时还具备完整的安全审计功能,能让我对谁能够访问我的数据、谁什么时候动了我的数据一目了然。

借助 KMS 的帮助,我们能够做到,即使我被脱裤了,攻击者拿到我的数据也无法解密!这篇文章,我们就来具体看看怎么将 KMS 应用到实践中,从而提升产品安全性。

这里我们将产品分为两种:

  1. 一般类型产品:需要保护自身数据,比如各种自营电商平台等。

  2. 平台类产品:平台服务商需要保护租户数据,比如 Authing、有赞 等。

之所以这么区分,是因为平台服务商属于多租户场景。对于一般类型产品,只需要加密好数据就行了,而对于平台类产品,理想情况下,租户希望做到以下两点:

  1. 知道平台服务商什么时候、以什么理由访问了我的数据。

  2. 当我不再使用你的服务时,我可以一键取消对平台服务商的数据授权,今后平台服务商再也无法访问到我的数据。

这种情况下,可以使用 KMS 跨账号授权的功能,基本流程为:租户在 AWS KMS 后台创建一个密钥并授权给平台服务商,平台服务商使用这个密钥加密/解密该租户的数据,借助 AWS Cloud Trail ,租户可以很轻松地知道平台服务商什么时候、以什么理由访问了我的数据。当租户决定不再使用平台服务商产品的时候,只需要在 AWS KMS 后台取消对其授权就行了。

基本原理就这么简单,下面从技术角度,梳理一下需要解决的几个技术问题:

  1. 如何使用 KMS 加密/解密的数据?

  2. 如何将 KMS 密钥授权给其他账号?

  3. 如何使用 Cloud Trail 查看第三方什么时候访问了我的数据?

  4. 作为平台服务商,如何解决 KMS 服务商绑定问题?

  5. 最后一点也是比较有难度的一点:如何在不大幅变动业务逻辑的情况下做到对数据自动加密解密?

下面我们由浅入深地介绍如何将 KMS 应用到你的产品中。


  1. 开通 KMS 服务,创建一个 KMS CKM

首先你需要有一个 AWS 账号,创建一个具备 KMS 全部权限的 User(或者 Role),该 User 继承的 Policy 如下:

{ 
 "Version": "2012-10-17", 
 "Statement": [
        {
 "Sid": "VisualEditor0",
 "Effect": "Allow",
 "Action": "kms:*",
 "Resource": "*"
        }
    ]
}


创建好 User 之后,你应该能够得到该 User 的accessKeyId和secretAccessKey,接下来在 SDK 中会用到。

接着在 AWS Console KMS 管理页面,创建一个客户管理的密钥 (Custom Managed Key),并将此密钥的「密钥管理员」和「可在加密操作中使用 CMK 的 IAM 用户和角色」设置为前一步创建的 User,创建之后你可以得到该密钥的 KeyId:

总结一下,这一步我们获取了以下这些内容:accessKeyId secretAccessKey 和 KeyId。


2. KMS 加密/解密 Demo

我们一般使用AWS Encryption SDK来完成数据的加密解密,它为我们节省了什么工作量,同时自带了很多最佳实践,如使用信封加密、 kerying 等。

AWS Encryption SDK 支持多种语言,如 Java/Python/C/JavaScript 等,这里我们以 JavaScript 为例。

加密 Demo:

import * as AWS from "aws-sdk"
import { KmsKeyringNode, encrypt, decrypt, getClient, MessageHeader } from '@aws-crypto/client-node'


 const keyring = new KmsKeyringNode({
  generatorKeyId: "给你的 KeyId",
  clientProvider: getClient(AWS.KMS, {
      credentials: {
        accessKeyId: config.accessKeyId,
        secretAccessKey: config.secretAccessKey,
      }
    })
 })
 const { result } = await encrypt(keyring, "需要加密的内容", { encryptionContext: {
    key: "额外上下文信息"
} })


说明以下几点:

  1. generatorKeyId就是 CMK 密钥 ID

  2. 这里初始化了一个 keyring 。

  3. encryptionContext是可选的加密上下文信息,可以在 Cloud Trail 审计页面看到。

解密 Demo:

import * as AWS from "aws-sdk"
import { KmsKeyringNode, encrypt, decrypt, getClient, MessageHeader } from '@aws-crypto/client-node'


 const keyring = new KmsKeyringNode({
  generatorKeyId: "你的 KeyId",
  clientProvider: getClient(AWS.KMS, {
      credentials: {
        accessKeyId: config.accessKeyId,
        secretAccessKey: config.secretAccessKey,
      }
    })
 })
const { plaintext, messageHeader } = await decrypt(keyring, encryptedData)


3. 如何将密钥授权给其他 AWS 账号

在创建 CMK 「定义密钥使用权限」步骤时,可以指定需要授权的第三方 AWS 账号,填入对方的 AWS 账号 ID 即可。

被授权方,可以按照如下方式使用此 CMK:

  1. 创建一个具备全部 CKM 操作权限的 User

  2. 将用户继承以下 Policy: Resource 为 CMK 的 arn

    {
     "Version": "2012-10-17",
     "Statement": [
        {
     "Sid": "Allow Use Of CMK In External Account",
     "Effect": "Allow",
     "Action": [
     "kms:Encrypt",
     "kms:Decrypt",
     "kms:ReEncrypt*",
     "kms:GenerateDataKey*",
     "kms:DescribeKey"
          ],
     "Resource": "arn:aws:kms:ap-northeast-1:xxxxxxxxx:key/b09ea52c-7262-4e60-b775-xxxxxx"
        }
      ]
    }


3. 借助 SDK 使用 CMK 进行数据加密、解密。

4. 当授权方不再希望被授权方使用此 CMK 时,在密钥管理页面将对方账号 ID 移除。


4. 利用 Cloud Trail 查看密钥审计

创建一个 Trail:

默认情况下,接下来你的密钥所有使用情况都会出现在 Event History 中了:

如上图所示,你可以看到 Decrypt(解密) 记录,以及请求详情:

  • userIdentity 就是被授权方。


5. 作为平台服务商,如何解决 KMS 服务商绑定问题

市面上常见的的 KMS 服务商大致有:

  • AWS KMS

  • 阿里云 KMS

  • 腾讯云 KMS

作为平台服务商,我们最好能够提供给租户选择 KMS 服务商的权利,而各家 KMS 服务商的使用方法存在差异,所以我们就需要做一下代码层面的封装,比如可以设置 kms, aliyun, aws, tecent 四个模块,在 kms 模块暴露 encrypt 和 decrypt 方法,根据租户配置的 KMS 服务商调用对应实际方法。

6. 如何在尽量不改动已有代码前提下,对数据完成加密、解密

如果前期代码设计的不够好,会出现需要大量修改已有查询、插入业务代码情况,这种成本是很大的。所以我们需要思考一下其他的解决方案:

  1. 订阅数据库日志,也即 MySQL 的 binlog、 PostgreSQL的Write-Ahead Logging (WAL)、mongodb 的 change streams。

  2. 利用数据库 ORM 的 hook,如typeorm Entity Listeners and Subscribersmongoose middlewares等,在插入数据前加密、读取数据之后解密。


下面给一个 mongoose 的示例代码:

schema.post(['find', 'findOne'], async function (docs, next) {
 if (!Array.isArray(docs)) {
 docs = [docs];
  }
 for (let doc of docs) {
 // 对数据进行解密
  }
 next()
})


schema.pre('findOneAndUpdate', async function (next) {
 // 对数据进行加密
})


schema.post('findOneAndUpdate', async function (doc, next) {
 // 对数据进行解密
})


总结

脱裤事件层出不穷,一个脱裤事件背后就是一家企业的灾难,我们都需要为用户的数据负责,尤其是平台服务商。在目前看来,KMS 不失为一个很好的选择。

获取更多资讯,请访问Authing 官网




文章作者

avatar

Authing 官方

0

文章总数

authing blog rqcode
关注 Authing 公众号
随时随地发现更多内容
authing blog rqcode
添加 Authing 小助手
加入 Authing 开发者大家庭
身份顾问在线解答
当前在线
如何打造完整的身份体系?
立即沟通
authing
添加企业微信,领取行业资料
authing
authing
下载 Authing 令牌,体验快速登录认证!
免费使用
在线咨询
电话咨询