之前的文章 ”CryptDB原理概述“ 介紹了CryptDB的基本原理,接下來從代碼的角度介紹其實現原理。本文首先關注mysql-proxy的lua腳本與CryptDB加密庫的交互過程。
前期準備
在進行源碼閱讀和調試之前,首先需要進行CryptDB的安裝。 之前已經對CryptDB在ubuntu 16.04上的安裝做過介紹。也可以使用我在github上共享的項目:https://github.com/yiwenshao/Practical-Cryptdb,里面對原始的安裝腳本做了小改,在ubuntu16.04只要執行INSTALL.sh 就可以完成全部的安裝工作。
安裝完成以后,首先執行如下的命令:
mkdir shadow
mysql-proxy --defaults-file=./mysql-proxy.cnf --proxy-lua-script=wrapper.lua
啟動mysql-proxy[1],然后就可以通過MySQL的客戶端連接mysql-proxy來完成數據庫操作。 所有SQL語句首先經過mysql-proxy的加密處理, 然后轉發給MySQL服務器。 對于MySQL服務器的返回結果, 也是先轉發給mysql-proxy, 經過解密處理以后返回給客戶端。
mysql-proxy通過lua腳本調用CryptDB的加密庫來完成SQL語句的加密操作,而上面建立的shadow目錄則是為了保存一個embedded模式的MySQL數據庫, 里面存儲了密鑰以及加密洋蔥的結構的等相關信息。
所以,接下來,我們先從lua腳本的入口出發,看一個SQL語句在加密過程中會經過哪些模塊。
mysql-proxy的與CryptDB的交互
mysql-proxy提供了如下幾個函數, 在客戶端執行SQL的不同階段, 會調用這幾個函數, 這些函數實現在 wrapper.lua 中:
- read_auth()
- disconnect_client()
- read_query(packet)
- read_query_result(inj)
在上面的四個函數的實現中,調用了CryptDB的加密庫中的幾個函數,這些函數實現在 mysqlproxy/ConnectWrapper.cc 中,分別是:
- connect
- disconnect
- rewrite
- next
所以,所有的CryptDB操作,都是以上面的8個函數為基礎的。對于CryptDB加密庫而言,里面的connect以及disconnect 只在客戶端建立連接和客戶端離開的時候被調用,所有的加解密功能都是通過rewrite和next這兩個函數來完成,這兩個函數就是CryptDB所有功能的入口。
回到mysql-proxy的lua腳本中的四個函數,可以分以下三個階段來介紹。
客戶端建立連接
在這個階段,lua腳本中的函數read_auth被調用,其內部調用CryptDB的函數connect. 在這個階段,需要為每個客戶端建立一個WrapperState結構來保存相關的信息。不同的客戶端通過ip+port來標識,多個客戶端的信息則通過一個如下的map結構來進行保存:
std::map<std::string, WrapperState*> clients;
要使用加密庫,還需要進行適當的初始化,并且多個client之間有共享狀態。所以,如果當前連接進來的是第一個客戶端,則需要對這個共享狀態進行初始化,其對應的是一個變量:
SharedProxyState * shared_ps//多個client的共享狀態
客戶端離開
當客戶端關閉的時候,lua腳本中的disconnect_client函數被調用。其內部調用CryptDB的disconnect函數。這個階段會把map中保存的客戶端的信息刪除。
客戶端的命令執行流程
客戶端發送命令給mysql-proxy的時候,lua腳本中的read_query(packet)函數被調用,參數packet中包含了SQL命令。MySQL執行結果返回的時候,lua腳本中的read_query_result(inj)函數被調用,參數inj包含了返回結果。
- 首先來看read_query函數。
當read_query 函數獲取到客戶端發送的明文SQL 命令的時候,會調用lua腳本中的read_query_real函數,其內部首先調用CryptDB庫中的rewrite函數,完成SQL語句的改寫。改寫后的SQL語句保存在之前介紹過的 clients 結構中。然后調用lua腳本中的next _handler 函數,其內部調用CryptDB庫中的next函數。在next函數中,首先執行函數獲得一個參數result_type,分為三種情況,根據不同的結果選擇不同的執行流程,包含了SQL命令執行的所有情況,分別如下:
//mysqlproxy/ConnectWrapper.cc
switch (result_type) {
case AbstractQueryExecutor::ResultType::QUERY_COME_AGAIN: {
//返回sql語句給lua腳本,執行結果再次進入該函數處理
}
case AbstractQueryExecutor::ResultType::QUERY_USE_RESULTS:{
//返回sql語句給lua腳本,執行結果直接返回客戶端
}
case AbstractQueryExecutor::ResultType::RESULTS:{
//返回解密結果給lua腳本,并由mysql-proxy返回給客戶端
}
}
對于一般的SQL語句的執行,分為兩種情況,第一種是直接進入QUERY_USE_RESULTS分支,返回SQL語句給lua腳本,SQL執行的結果直接返回給客戶端。 第二種是進入第一個分支QUERY_COME_AGAIN,返回SQL語句給lua腳本轉發到MySQL執行,返回的結果再次進入next函數,執行并且進入第三個分支,返回解密的結果給客戶端。
另外,對于有些語句,并不需要調用rewrite函數進行加密,在lua腳本的階段直接過濾了,這些情況就更加簡單。
- 然后來看read _query_results階段。
當上面介紹的加密SQL語句發送給MySQL執行,并返回執行結果給mysql-proxy的時候,會調用lua腳本中的read _query_results(inj)函數。如果在read_query階段進入了第二個分支,那么lua腳本會設置一個全局變量skip為true,read_query_results的處理就被跳過,直接返回結果給客戶端。如果在read_query階段進入了第一個分支,則會在這里再次調用next_handler函數,從而進入next函數,再次執行并進入switch分支的判斷流程。
兩個執行的例子
一些解密的細節以及類的介紹將在后續的文章中給出。在這里,給出兩個SQL語句執行的例子,用于說明執行過程中lua腳本以及CryptDB庫中的幾個函數的調用過程,以及幾個主要執行分支的含義。
- show databases;
該命令的處理流程:
首先進入read_query,內部調用CryptDB的rewrite函數進行加密,然后調用lua中的next_handler,內部調用CryptDB的next函數,根據上面介紹的,進入switch的第二個分支,表示執行命令的結果不需要處理,直接返回給客戶端。然后給lua腳本傳遞改寫以后的命令。
再次回到lua腳本的next_handler函數,其處理了query results分支,將獲得的命令轉發給MySQL執行。執行完成以后的結果返回給mysql-proxy的時候,會調用read_query_result函數。根據上面介紹的,由于在read_query階段進入了第二個分支,這里的處理會被跳過,也就是不做任何處理,結果直接返回給客戶端。這樣,客戶端就得到了show databases的執行結果。
- select * from student;
我們假設已經有了一張表,對其進行select操作,其對應的執行流程如下。
首先進入read_query,內部調用CryptDB的rewrite函數進行SQL語句的加密,返回以后調用lua中的next_handler函數,內部調用CryptDB的next函數,根據上面介紹的,進入第一個分支:QUERY_COME_AGAIN。返回加密以后的SQL命令給lua腳本。
在lua腳本中,next_handler處理了again分支,發送加密命令給MySQL,獲得select返回的加密結果以后,lua腳本中的read_query_results被調用。由于在read_query階段進入了第一個分支,這里會繼續調用next_handler函數,并進入到next函數,執行獲得result_type,然后這次進入到RESULTS分支。在lua腳本中,處理了results分支,將解密后的結果返回給客戶端,這樣就完成了整個流程。
總結
本文介紹了SQL加密執行過程中,mysql-proxy使用的lua腳本與CryptDB的加密庫的交互過程,其中主要的幾個函數是lua腳本中的read_query,read_query_results,以及CryptDB庫中的rewrite和next。代碼位于wrapper.lua以及mysqlproxy/ConnectWrapper.cc中。
參考文獻
- 1. MySQL-Proxy相關: https://downloads.mysql.com/docs/mysql-proxy-en.pdf
- CryptDB的原理介紹: https://yiwenshao.github.io/2017/05/01/Cryptdb原理概述/#more
- CryptDB的安裝腳本: https://github.com/yiwenshao/Practical-Cryptdb
- 作者的wiki說明: https://github.com/cryptdb-org/cryptdb-wiki/wiki/New-Pipeline
原始鏈接:yiwenshao.github.io/2018/02/26/CryptDB代碼分析1-lua與加密庫/
文章作者:Yiwen Shao
許可協議: Attribution-NonCommercial 4.0
轉載請保留以上信息, 謝謝!