GrowingIO 資料安全實踐

語言: CN / TW / HK

什麼是資料安全

根據《中華人民共和國資料安全法》中第三條,給出了資料安全的定義,是指通過採取必要措施,確保資料處於有效保護和合法利用的狀態,以及具備保障持續安全狀態的能力。

為什麼企業要做資料安全

在網際網路盛行的今天,不法分子可以通過網路攻擊,網路欺騙等手段竊取使用者的個人資訊甚至企業的機密資訊。而且在拿到部分使用者資訊後就可以唯一的鎖定具體某一個人,因此資料的保密顯得格外重要。

GrowingIO 資料安全落地

為了保證客戶資料的安全性, GrowingIO 通過構建一個安全的軟體執行時與資料靜態儲存加密來提高資料生產安全性。下面我們將詳細介紹資料安全落地的過程。

【軟體執行安全】

軟體執行安全也就是企業的系統執行安全,這主要包含兩個方面: 資料邏輯隔離:系統通過使用者的許可權與角色,給出專有的資料操作集合。 KMS祕鑰管理:系統所依賴的資料庫,中介軟體不會因為祕鑰的洩露而造成資料洩露。

資料邏輯隔離

資料邏輯隔離主要是對平臺上操作的使用者進行身份,角色,許可權的認證。 認證流程:

所有平臺的使用者通過RBAC 許可權模型分配不同的角色和許可權。只有擁有對應角色許可權的使用者才能檢視或進行對應的操作。

KMS 祕鑰管理

KMS 即(key manage system)祕鑰管理系統。目前亞馬遜雲和阿里雲等雲產品都有自己的解決方案。KMS 支援多種型別的的資料庫,中介軟體,以及應用祕鑰管理等功能。使用KMS 之後所有資料庫,中介軟體等的使用者名稱,密碼對產研均不可見,應用靜態資料加密對應的祕鑰也不可見。 目前支援資料庫和中介軟體以及系統祕鑰:

【互動流程】

【示例展示】

以亞馬遜雲(AWS)為例: 應用配置

kms:
  enabled: false
  aws:
      region: ""
      access_key_id: ""
      secret_access_key: ""
#應用中新增
spring:
  redis:
    kms-key: "test/json/redis"
  datasource:
    kms-key: "test/postgresql/accounts"

執行初始化

示例以postgresql 資料庫HikariCP 連線池為例:

@Bean
@ConditionalOnProperty(value = "kms.enabled",havingValue = "true")
public HikariDataSource dataSource(DataSourceProperties properties, KmsProperties kms,Configs configs) {
      SecretsManagerClient client = SecretsManagerClientBuilder.build(kms.getRegion(),kms.getAwsAccessKeyId(),kms.getAwsSecretAccessKey());
      AwsSecretProvider secretProvider = new AwsSecretProvider(client);
      String secret;
      try{
          secret = secretProvider.getSecret(configs.getDataSourceKmsKey());
          //secret是json格式的連結資訊
          final HashMap<String,String> map = Jackson.readValue(secret, HashMap.class);
          String url = String.format("jdbc:postgresql://%s:%s/%s?useUnicode=true&useSSL=false&characterEncoding=utf8",map.get("host"),map.get("port"),map.get("dbname"));
          properties.setUrl(url);
          properties.setUsername(map.get("username"));
          properties.setPassword(map.get("password"));
      }catch (Exception e) {
          log.error("kms database error",e);
      }
      HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
      if (StringUtils.hasText(properties.getName())) {
          dataSource.setPoolName(properties.getName());
      }
      return dataSource;
  }

在實際開發中用到的任何資料庫,中介軟體都可以通過KMS 來管理對應的使用者名稱,密碼等敏感資訊。

【靜態儲存安全】

PII 管理

【什麼是PII】

PII 即個人身份資訊 (Personally identifiable information) 是任何可能識別特定個人的資料。任何可用於將一個人與另一個人區分開來並可用於對以前匿名資料進行去匿名化的資訊都可以被視為 PII。 PII 可以單獨使用或與其他相關資料一起使用來識別個人,並且包含可以唯一識別個人的直接識別符號(例如護照資訊)或準識別符號(例如種族),可以與其他準識別符號結合使用識別符號,如出生日期,以成功識別個人。

【PII 意義】

保護 PII 對於個人隱私、資料隱私、資料保護、資訊隱私和資訊保安至關重要。僅憑個人資訊的一小部分,竊賊就可以以該人的名義建立虛假賬戶、產生債務、偽造護照或將個人身份出售給犯罪分子。隨著個人的個人資料每天被記錄、跟蹤和使用——例如在使用指紋的生物識別掃描和用於解鎖裝置的面部識別系統中——保護個人身份和他們獨有的任何識別資訊變得越來越重要。

【PII 加解密】

PII 加密: 對於所有入庫的使用者敏感資料使用加密演算法進行加密。 PII 解密:普通使用者只能檢視加密的密文資料,對於有業務需要的通過授權可以檢視明文資料。

GrowingIO 預設選擇AES(AES/CBC/PKCS5Padding) 演算法 ,採用256長度的祕鑰來作為PII 資料加密的實現。

執行流程

資料加密

資料解密

示例展示

PII 可以從配置中心或KMS 中來獲取加解密需要的祕鑰資料。

祕鑰獲取:

// KMS 方式
SecretsManagerClient client = SecretsManagerClientBuilder.build(kms.getRegion(),kms.getAwsAccessKeyId(),kms.getAwsSecretAccessKey());
AwsSecretProvider secretProvider = new AwsSecretProvider(client);
String secret = secretProvider.getSecret("test/pii/json");
//secret是json格式的連結資訊
final HashMap<String,String> map = Jackson.readValue(secret, HashMap.class);

String algorithmIv = map.getOrElse("algorithm_iv", "");
// 向量
String iv = DatatypeConverter.parseHexBinary(algorithmIv);
// 祕鑰
String encryptionKey = DatatypeConverter.parseHexBinary(map("encryption_key"));
String decryptionKey = DatatypeConverter.parseHexBinary(map("decryption_key"));

// 配置中心方式
// 向量
String iv = Hex.decodeHex(Configs.Encry.configCenterIv);
// 祕鑰
String secret = Hex.decodeHex(Configs.Encry.configCenterSecret);

執行加密或者解密操作:

通過PII 進行資料處理後,資料庫中敏感資料全為加密儲存。頁面渲染依據使用者許可權判斷是否加密或者解密展示。

資料儲存與渲染

資料庫資料:

只有管理員登入後渲染解密後的資料:

常見問題

一、向量IV 的支援

在AES 加密演算法中:AES_ECB_PKCS5Padding 不支援向量,AES_CBC_PKCS5Padding支援向量且安全性更高。如果原來已經有用到AES 演算法要考慮相容性。

二、Base 64 編碼帶來的問題

現象:如果加密的字串比較長,加密後的密文將長度超過76時,密文中會被加入一個換行符,導致後續按行解析異常。

在JDK1.8 之後Base64 工具類移到了java.util 包中,為了向低版本的JDK 相容,Base64 中 MimeEncode,採用的方式:

private static final int MIMELINEMAX = 76;
private static final byte[] CRLF = new byte[] {'\r', '\n'};
static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);

也就是說在加密串的長度超過76時會加上 '\r' 或者 '\n',這就導致如果按行進行資料的某些操作將會發生致命錯誤。所以推薦使用Base64中Encode的實現:

static final Encoder RFC4648 = new Encoder(false, null, -1, true);
static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);

這樣不管加密後的字串長度有多長都不會發生換行的問題。

參考JDK 原始碼和祕鑰標準:

RFC 4648 http://www.ietf.org/rfc/rfc4648.txt 

RFC 2045 http://www.ietf.org/rfc/rfc2045.txt