透明化Sharding-JDBC數據庫字段加解密方案

前言

近期,博主公司應安全審計要求,需要對數據庫中的用戶關鍵信息做加密處理,這樣,即使生產數據被脫褲,也不會泄露用戶的敏感信息,在做瞭初步的需求歸納和功能分析後,我們制定瞭簡單的開發方案,將需要加解密的字段的元數據信息通過配置或註解的方式標記出來,嘗試使用hibernate的filter和Interceptor針對用戶sql做攔截,做到透明化加解密。但是這個方案很快被否決瞭,查詢結果集沒法通過這種方式達到目的。然後將方向轉向瞭代理JDBC驅動的方式。

在摸索JDBC代理方案過程中發現,業界已經有瞭非常成熟的針對數據庫字段透明化加解密的方案,而且和我們場景以及方案非常相符,整體方案如下:

背景

安全控制一直是治理的重要環節,數據脫敏屬於安全控制的范疇。對互聯網公司、傳統行業來說,數據安全一直是極為重視和敏感的話題。數據脫敏是指對某些敏感信息通過脫敏規則進行數據的變形,實現敏感隱私數據的可靠保護。涉及客戶安全數據或者一些商業性敏感數據,如身份證號、手機號、卡號、客戶號等個人信息按照相關部門規定,都需要進行數據脫敏。

在真實業務場景中,相關業務開發團隊則往往需要針對公司安全部門需求,自行實行並維護一套加解密系統,而當脫敏場景發生改變時,自行維護的脫敏系統往往又面臨著重構或修改風險。此外,對於已經上線的業務,如何在不修改業務邏輯、業務SQL的情況下,透明化、安全低風險地實現無縫進行脫敏改造呢?

Apache ShardingSphere根據業界對脫敏的需求及業務改造痛點,提供瞭一套完整、安全、透明化、低改造成本的數據脫敏整合解決方案。

前序

Apache ShardingSphere是一套開源的分佈式數據庫中間件解決方案組成的生態圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(規劃中)這3款相互獨立,卻又能夠混合部署配合使用的產品組成。它們均能夠提供標準化的數據分片、分佈式事務和分佈式治理功能,可適用於如Java同構、異構語言、容器、雲原生等各種多樣化的應用場景。

數據脫敏模塊屬於ShardingSphere分佈式治理這一核心功能下的子功能模塊。它通過對用戶輸入的SQL進行解析,並依據用戶提供的脫敏配置對SQL進行改寫,從而實現對原文數據進行加密,並將原文數據(可選)及密文數據同時存儲到底層數據庫。在用戶查詢數據時,它又從數據庫中取出密文數據,並對其解密,最終將解密後的原始數據返回給用戶。Apache ShardingSphere分佈式數據庫中間件自動化&透明化瞭數據脫敏過程,讓用戶無需關註數據脫敏的實現細節,像使用普通數據那樣使用脫敏數據。此外,無論是已在線業務進行脫敏改造,還是新上線業務使用脫敏功能,ShardingSphere都可以提供一套相對完善的解決方案。

需求場景分析

對於數據脫敏的需求,在現實的業務場景中一般分為兩種情況:

1.新業務上線,安全部門規定需將涉及用戶敏感信息,例如銀行、手機號碼等進行加密後存儲到數據庫,在使用的時候再進行解密處理。因為是全新系統,因而沒有存量數據清洗問題,所以實現相對簡單。

2.已上線業務,之前一直將明文存儲在數據庫中。相關部門突然需要對已上線業務進行脫敏整改。這種場景一般需要處理三個問題:

  • a) 歷史數據需要如何進行脫敏處理,即洗數。
  • b) 如何能在不改動業務SQL和邏輯情況下,將新增數據進行脫敏處理,並存儲到數據庫;在使用時,再進行解密取出。
  • c) 如何較為安全、無縫、透明化地實現業務系統在明文與密文數據間的遷移。

處理流程詳解

整體架構

ShardingSphere提供的Encrypt-JDBC和業務代碼部署在一起。業務方需面向Encrypt-JDBC進行JDBC編程。由於Encrypt-JDBC實現所有JDBC標準接口,業務代碼無需做額外改造即可兼容使用。此時,業務代碼所有與數據庫的交互行為交由Encrypt-JDBC負責。業務隻需提供脫敏規則即可。作為業務代碼與底層數據庫中間的橋梁,Encrypt-JDBC便可攔截用戶行為,並在改造行為後與數據庫交互。

Encrypt-JDBC將用戶發起的SQL進行攔截,並通過SQL語法解析器進行解析、理解SQL行為,再依據用戶傳入的脫敏規則,找出需要脫敏的字段和所使用的加解密器對目標字段進行加解密處理後,再與底層數據庫進行交互。ShardingSphere會將用戶請求的明文進行加密後存儲到底層數據庫;並在用戶查詢時,將密文從數據庫中取出進行解密後返回給終端用戶。ShardingSphere通過屏蔽對數據的脫敏處理,使用戶無需感知解析SQL、數據加密、數據解密的處理過程,就像在使用普通數據一樣使用脫敏數據。

脫敏規則

在詳解整套流程之前,我們需要先瞭解下脫敏規則與配置,這是認識整套流程的基礎。脫敏配置主要分為四部分:數據源配置,加密器配置,脫敏表配置以及查詢屬性配置,其詳情如下圖所示:

數據源配置:是指DataSource的配置。

加密器配置:是指使用什麼加密策略進行加解密。目前ShardingSphere內置瞭兩種加解密策略:AES/MD5。用戶還可以通過實現ShardingSphere提供的接口,自行實現一套加解密算法。

脫敏表配置:用於告訴ShardingSphere數據表裡哪個列用於存儲密文數據(cipherColumn)、哪個列用於存儲明文數據(plainColumn)以及用戶想使用哪個列進行SQL編寫(logicColumn)。

如何理解用戶想使用哪個列進行SQL編寫(logicColumn)?

我們可以從Encrypt-JDBC存在的意義來理解。Encrypt-JDBC最終目的是希望屏蔽底層對數據的脫敏處理,也就是說我們不希望用戶知道數據是如何被加解密的、如何將明文數據存儲到plainColumn,將密文數據存儲到cipherColumn。換句話說,我們不希望用戶知道plainColumn和cipherColumn的存在和使用。所以,我們需要給用戶提供一個概念意義上的列,這個列可以脫離底層數據庫的真實列,它可以是數據庫表裡的一個真實列,也可以不是,從而使得用戶可以隨意改變底層數據庫的plainColumn和cipherColumn的列名。或者刪除plainColumn,選擇永遠不再存儲明文,隻存儲密文。隻要用戶的SQL面向這個邏輯列進行編寫,並在脫敏規則裡給出logicColumn和plainColumn、cipherColumn之間正確的映射關系即可。

為什麼要這麼做呢?答案在文章後面,即為瞭讓已上線的業務能無縫、透明、安全地進行數據脫敏遷移。

查詢屬性的配置:當底層數據庫表裡同時存儲瞭明文數據、密文數據後,該屬性開關用於決定是直接查詢數據庫表裡的明文數據進行返回,還是查詢密文數據通過Encrypt-JDBC解密後返回。

脫敏處理過程

舉個栗子,假如數據庫裡有一張表叫做t_user,這張表裡實際有兩個字段pwd_plain,用於存放明文數據、pwd_cipher,用於存放密文數據,同時定義logicColumn為pwd。那麼,用戶在編寫SQL時應該面向logicColumn進行編寫,即INSERT INTO t_user SET pwd = ‘123’。ShardingSphere接收到該SQL,通過用戶提供的脫敏配置,發現pwd是logicColumn,於是便對邏輯列及其對應的明文數據進行脫敏處理。可以看出**ShardingSphere將面向用戶的邏輯列與面向底層數據庫的明文列和密文列進行瞭列名以及數據的脫敏映射轉換。**如下圖所示:

**這也正是Encrypt-JDBC核心意義所在,即依據用戶提供的脫敏規則,將用戶SQL與底層數據表結構割裂開來,使得用戶的SQL編寫不再依賴於真實的數據庫表結構。而用戶與底層數據庫之間的銜接、映射、轉換交由ShardingSphere進行處理。**為什麼我們要這麼做?還是那句話:為瞭讓已上線的業務能無縫、透明、安全地進行數據脫敏遷移。

為瞭讓讀者更清晰瞭解到Encrypt-JDBC的核心處理流程,下方圖片展示瞭使用Encrypt-JDBC進行增刪改查時,其中的處理流程和轉換邏輯,如下圖所示。

解決方案詳解

在瞭解瞭ShardingSphere脫敏處理流程後,即可將脫敏配置、脫敏處理流程與實際場景進行結合。所有的設計開發都是為瞭解決業務場景遇到的痛點。那麼面對之前提到的業務場景需求,又應該如何使用ShardingSphere這把利器來滿足業務需求呢?

新上線業務

業務場景分析:新上線業務由於一切從零開始,不存在歷史數據清洗問題,所以相對簡單。

解決方案說明:選擇合適的加密器,如AES後,隻需配置邏輯列(面向用戶編寫SQL)和密文列(數據表存密文數據)即可,邏輯列和密文列可以相同也可以不同。建議配置如下(Yaml格式展示):

encryptRule:  encryptors:  aes_encryptor:  type: aes  props: aes.key.value: 123456abc  tables:  t_user:  columns:  pwd:  cipherColumn: pwd  encryptor: aes_encryptor

使用這套配置,Encrypt-JDBC隻需將logicColumn和cipherColumn進行轉換,底層數據表不存儲明文,隻存儲瞭密文,這也是安全審計部分的要求所在。如果用戶希望將明文、密文一同存儲到數據庫,隻需添加plainColumn配置即可。整體處理流程如下圖所示:

已上線業務改造

業務場景分析:由於業務已經在線上運行,數據庫裡必然存有大量明文歷史數據。現在的問題是如何讓歷史數據得以加密清洗、如何讓增量數據得以加密處理、如何讓業務在新舊兩套數據系統之間進行無縫、透明化遷移。

解決方案說明:在提供解決方案之前,我們先來頭腦風暴一下:首先,既然是舊業務需要進行脫敏改造,那一定存儲瞭非常重要且敏感的信息。這些信息含金量高且業務相對基礎重要。如果搞錯瞭,整個團隊KPI就再見瞭。所以不可能一上來就停業務,禁止新數據寫入,再找個加密器把歷史數據全部加密清洗,再把之前重構的代碼部署上線,使其能把存量和增量數據進行在線加密解密。如此簡單粗暴的方式,按照歷史經驗來談,一定涼涼。

那麼另一種相對安全的做法是:重新搭建一套和生產環境一模一樣的預發環境,然後通過相關遷移洗數工具把生產環境的存量原文數據加密後存儲到預發環境,而新增數據則通過例如MySQL主從復制及業務方自行開發的工具加密後存儲到預發環境的數據庫裡,再把重構後可以進行加解密的代碼部署到預發環境。這樣生產環境是一套以明文為核心的查詢修改的環境;預發環境是一套以密文為核心加解密查詢修改的環境。在對比一段時間無誤後,可以夜間操作將生產流量切到預發環境中。此方案相對安全可靠,隻是時間、人力、資金、成本較高,主要包括:預發環境搭建、生產代碼整改、相關輔助工具開發等。除非無路可走,否則業務開發人員一般是從入門到放棄。

業務開發人員最希望的做法是:減少資金費用的承擔、最好不要修改業務代碼、能夠安全平滑遷移系統。於是,ShardingSphere的脫敏功能模塊便應用而生。可分為三步進行:

系統遷移前

假設系統需要對t_user的pwd字段進行脫敏處理,業務方使用Encrypt-JDBC來代替標準化的JDBC接口,此舉基本不需要額外改造(我們還提供瞭SpringBoot,SpringNameSpace,Yaml等接入方式,滿足不同業務方需求)。另外,提供一套脫敏配置規則,如下所示:

encryptRule:  encryptors:  aes_encryptor:  type: aes  props: aes.key.value: 123456abc  tables:  t_user:  columns:  pwd:  plainColumn: pwd  cipherColumn: pwd_cipher  encryptor: aes_encryptor props: query.with.cipher.column: false

依據上述脫敏規則可知,首先需要在數據庫表t_user裡新增一個字段叫做pwd_cipher,即cipherColumn,用於存放密文數據,同時我們把plainColumn設置為pwd,用於存放明文數據,而把logicColumn也設置為pwd。由於之前的代碼SQL就是使用pwd進行編寫,即面向邏輯列進行SQL編寫,所以業務代碼無需改動。通過Encrypt-JDBC,針對新增的數據,會把明文寫到pwd列,並同時把明文進行加密存儲到pwd_cipher列。此時,由於query.with.cipher.column設置為false,對業務應用來說,依舊使用pwd這一明文列進行查詢存儲,卻在底層數據庫表pwd_cipher上額外存儲瞭新增數據的密文數據,其處理流程如下圖所示:

新增數據在插入時,就通過Encrypt-JDBC加密為密文數據,並被存儲到瞭cipherColumn。而現在就需要處理歷史明文存量數據。由於Apache ShardingSphere目前並未提供相關遷移洗數工具,此時需要業務方自行將pwd中的明文數據進行加密處理存儲到pwd_cipher。

系統遷移中

新增的數據已被Encrypt-JDBC將密文存儲到密文列,明文存儲到明文列;歷史數據被業務方自行加密清洗後,將密文也存儲到密文列。也就是說現在的數據庫裡即存放著明文也存放著密文,隻是由於配置項中的query.with.cipher.column=false,所以密文一直沒有被使用過。現在我們為瞭讓系統能切到密文數據進行查詢,需要將脫敏配置中的query.with.cipher.column設置為true。在重啟系統後,我們發現系統業務一切正常,但是Encrypt-JDBC已經開始從數據庫裡取出密文列的數據,解密後返回給用戶;而對於用戶的增刪改需求,則依舊會把原文數據存儲到明文列,加密後密文數據存儲到密文列。

雖然現在業務系統通過將密文列的數據取出,解密後返回;但是,在存儲的時候仍舊會存一份原文數據到明文列,這是為什麼呢?答案是:為瞭能夠進行系統回滾。**因為隻要密文和明文永遠同時存在,我們就可以通過開關項配置自由將業務查詢切換到cipherColumn或plainColumn。**也就是說,如果將系統切到密文列進行查詢時,發現系統報錯,需要回滾。那麼隻需將query.with.cipher.column=false,Encrypt-JDBC將會還原,即又重新開始使用plainColumn進行查詢。處理流程如下圖所示:

系統遷移後

由於安全審計部門要求,業務系統一般不可能讓數據庫的明文列和密文列永久同步保留,我們需要在系統穩定後將明文列數據刪除。即我們需要在系統遷移後將plainColumn,即pwd進行刪除。那問題來瞭,現在業務代碼都是面向pwd進行編寫SQL的,把底層數據表中的存放明文的pwd刪除瞭,換用pwd_cipher進行解密得到原文數據,那豈不是意味著業務方需要整改所有SQL,從而不使用即將要被刪除的pwd列?還記得我們Encrypt-JDBC的核心意義所在嗎?

這也正是Encrypt-JDBC核心意義所在,即依據用戶提供的脫敏規則,將用戶SQL與底層數據庫表結構割裂開來,使得用戶的SQL編寫不再依賴於真實的數據庫表結構。而用戶與底層數據庫之間的銜接、映射、轉換交由ShardingSphere進行處理。

是的,因為有logicColumn存在,用戶的編寫SQL都面向這個虛擬列,Encrypt-JDBC就可以把這個邏輯列和底層數據表中的密文列進行映射轉換。於是遷移後的脫敏配置即為:

其處理流程如下:

至此,已在線業務脫敏整改解決方案全部敘述完畢。我們提供瞭Java、Yaml、SpringBoot、SpringNameSpace多種方式供用戶選擇接入,力求滿足業務不同的接入需求。該解決方案目前已在京東數科不斷落地上線,提供對內基礎服務支撐。

中間件脫敏服務優勢

  • 自動化&透明化數據脫敏過程,用戶無需關註脫敏中間實現細節。
  • 提供多種內置、第三方(AKS)的脫敏策略,用戶僅需簡單配置即可使用。
  • 提供脫敏策略API接口,用戶可實現接口,從而使用自定義脫敏策略進行數據脫敏。
  • 支持切換不同的脫敏策略。
  • 針對已上線業務,可實現明文數據與密文數據同步存儲,並通過配置決定使用明文列還是密文列進行查詢。可實現在不改變業務查詢SQL前提下,已上線系統對加密前後數據進行安全、透明化遷移。

適用場景說明

  • 用戶項目使用Java語言進行編程。
  • 後端數據庫為MySQL、Oracle、PostgreSQL、SQLServer。
  • 用戶需要對數據庫表中某個或多個列進行脫敏(數據加密&解密)。
  • 兼容所有常用SQL。

限制條件

  • 用戶需要自行處理數據庫中原始的存量數據、洗數。
  • 使用脫敏功能+分庫分表功能,部分特殊SQL不支持,請參考SQL使用規范。
  • 脫敏字段無法支持比較操作,如:大於小於、ORDER BY、BETWEEN、LIKE等。
  • 脫敏字段無法支持計算操作,如:AVG、SUM以及計算表達式 。

加密策略解析

ShardingSphere提供瞭兩種加密策略用於數據脫敏,該兩種策略分別對應ShardingSphere的兩種加解密的接口,即ShardingEncryptor和ShardingQueryAssistedEncryptor。

一方面,ShardingSphere為用戶提供瞭內置的加解密實現類,用戶隻需進行配置即可使用;另一方面,為瞭滿足用戶不同場景的需求,我們還開放瞭相關加解密接口,用戶可依據該兩種類型的接口提供具體實現類。再進行簡單配置,即可讓ShardingSphere調用用戶自定義的加解密方案進行數據脫敏。

SHARDINGENCRYPTOR

該解決方案通過提供encrypt(), decrypt()兩種方法對需要脫敏的數據進行加解密。在用戶進行INSERT, DELETE, UPDATE時,ShardingSphere會按照用戶配置,對SQL進行解析、改寫、路由,並會調用encrypt()將數據加密後存儲到數據庫, 而在SELECT時,則調用decrypt()方法將從數據庫中取出的脫敏數據進行逆向解密,最終將原始數據返回給用戶。

當前,ShardingSphere針對這種類型的脫敏解決方案提供瞭兩種具體實現類,分別是MD5(不可逆),AES(可逆),用戶隻需配置即可使用這兩種內置的方案。

SHARDINGQUERYASSISTEDENCRYPTOR

相比較於第一種脫敏方案,該方案更為安全和復雜。它的理念是:即使是相同的數據,如兩個用戶的密碼相同,它們在數據庫裡存儲的脫敏數據也應當是不一樣的。這種理念更有利於保護用戶信息,防止撞庫成功。

它提供三種函數進行實現,分別是encrypt(), decrypt(), queryAssistedEncrypt()。在encrypt()階段,用戶通過設置某個變動種子,例如時間戳。針對原始數據+變動種子組合的內容進行加密,就能保證即使原始數據相同,也因為有變動種子的存在,致使加密後的脫敏數據是不一樣的。在decrypt()可依據之前規定的加密算法,利用種子數據進行解密。

雖然這種方式確實可以增加數據的保密性,但是另一個問題卻隨之出現:相同的數據在數據庫裡存儲的內容是不一樣的,那麼當用戶按照這個加密列進行等值查詢(SELECT FROM table WHERE encryptedColumnn = ?)時會發現無法將所有相同的原始數據查詢出來。為此,我們提出瞭輔助查詢列的概念。該輔助查詢列通過queryAssistedEncrypt()生成,與decrypt()不同的是,該方法通過對原始數據進行另一種方式的加密,但是針對原始數據相同的數據,這種加密方式產生的加密數據是一致的。將queryAssistedEncrypt()後的數據存儲到數據中用於輔助查詢真實數據。因此,數據庫表中多出這一個輔助查詢列。

由於queryAssistedEncrypt()和encrypt()產生不同加密數據進行存儲,而decrypt()可逆,queryAssistedEncrypt()不可逆。 在查詢原始數據的時候,我們會自動對SQL進行解析、改寫、路由,利用輔助查詢列進行 WHERE條件的查詢,卻利用 decrypt()對encrypt()加密後的數據進行解密,並將原始數據返回給用戶。這一切都是對用戶透明化的。

當前,ShardingSphere針對這種類型的脫敏解決方案並沒有提供具體實現類,卻將該理念抽象成接口,提供給用戶自行實現。ShardingSphere將調用用戶提供的該方案的具體實現類進行數據脫敏。

後續

本篇文章介紹瞭如何使用ShardingSphere產品之一的Encrypt-JDBC進行接入,接入形式還可以選擇使用SpringBoot、SpringNameSpace等,這種形態的接入端主要面向JAVA同構,並與業務代碼共同部署在生產環境中。面向異構語言,ShardingSphere還提供Encrypt-Proxy客戶端。Encrypt-Proxy是一款實現MySQL、PostgreSQL的二進制協議的服務器端產品,用戶可獨立部署Encrypt-Proxy服務,並且像使用普通MySQL、PostgreSQL數據庫一樣,使用例如Navicat第三方數據庫管理工具、JAVA連接池、命令行的方式訪問這臺具有脫敏功能的虛擬數據庫服務器。

脫敏功能屬於Apache ShardingSphere分佈式治理的功能范疇。事實上,Apache ShardingSphere這個生態還擁有其他更強大的能力,例如數據分片、讀寫分離、分佈式事務、監控治理等。您甚至可以選擇任意多種功能模塊進行疊加使用,例如同時使用數據脫敏+數據分片,或是數據分片+讀寫分離,再或者是監控治理+數據分片等。除瞭在功能層面的疊加選擇,ShardingSphere還提供瞭各種接入端形式,例如Sharding-JDBC或Sharding-Proxy等以滿足大傢不同場景需求。

以上就是多數據源模式JPA整合sharding-jdbc實現數據脫敏的詳細內容,更多關於多數據源模式JPA整合sharding-jdbc數據脫敏的資料請關註WalkonNet其它相關文章!

推薦閱讀: