數(shù)據(jù)安全治理關(guān)鍵技術(shù)之?dāng)?shù)據(jù)庫脫敏技術(shù)詳解
數(shù)據(jù)安全治理之API監(jiān)測系統(tǒng) ,解決API接口安全問題【安華金和】
新一代數(shù)據(jù)庫脫敏技術(shù),為敏感數(shù)據(jù)建立保護(hù)盾!
數(shù)據(jù)庫脫敏系統(tǒng)與金融行業(yè)案例解讀
數(shù)據(jù)安全治理建設(shè)思路的著力點(diǎn)——數(shù)據(jù)安全咨詢服務(wù)【安華金和】
數(shù)據(jù)庫防火墻功能有哪些?-數(shù)據(jù)安全-安華金和
數(shù)據(jù)安全關(guān)鍵技術(shù)之?dāng)?shù)據(jù)庫脫敏技術(shù)詳解【安華金和】
中國數(shù)據(jù)安全治理落地指導(dǎo)書籍《數(shù)據(jù)安全治理白皮書5.0》正式發(fā)布(附下載)
參數(shù)綁定是數(shù)據(jù)庫編程中常用的一種方法,通過這種方法,數(shù)據(jù)庫系統(tǒng)可以減少編譯次數(shù),快速執(zhí)行,提升效率;但這種編程方法將對數(shù)據(jù)庫的審計帶來挑 戰(zhàn),在筆者所見到的若干數(shù)據(jù)庫審計產(chǎn)品中,在這種情況下都出了不少的錯誤。有的是漏審了語句,有的是記錄下了操作的語句,但將具體執(zhí)行時所使用的參數(shù)記錯 了或漏記了。這些缺陷對于審計產(chǎn)品無疑很是致命。
為了詳解這種情況,我們來看一下參數(shù)綁定的基本概念。我們在常規(guī)的圖形化或命令行工具中,往往都是直接寫上SQL語句,比如:
Select * from person_info where id=’12XXXXX6722’;
在這里查詢條件是身份證號碼。根據(jù)身份證號碼查詢個人信息,是一種常用功能,也是會重復(fù)使用的語句,為了提升效率,編程中可以這么寫:
String sql1=’Select * from person_info where id=?;’
PreparedStatement pStmt = testConn.getConnection().prepareStatement(sql);
pStmt.setInt(1, ’12XXXXX6722’);
pStmt.execute();
下一次再使用時,就不用再發(fā)送語句了,可以直接發(fā)送:
pStmt.setInt(1, ’22XXXXX5399’);
pStmt.execute();
對于數(shù)據(jù)庫審計系統(tǒng)而言,單純地記錄下來‘Select * from person_info where id=?’是存在缺陷的,因為你無法明確額操作人員到底訪問了哪個用戶的信息,必須明確下來具體的參數(shù)才行。
這就要求將設(shè)定的參數(shù),與Prepare的語句有效的關(guān)聯(lián),形成可視化的審計記錄展現(xiàn):
Select * from person_info where id=’12XXXXX6722’;
Select * from person_info where id=’22XXXXX5399’;
這實際上要求審計系統(tǒng)比起單純的記錄語句要完成更多的工作;其中一個重要任務(wù)的就是句柄追蹤,本質(zhì)上SQL語句的執(zhí)行過程追蹤就是句柄追蹤過程。在上面顯示的例子中
pStmt.execute(),在通訊過程中并不發(fā)送具體的語句,而僅是告知服務(wù)器要執(zhí)行哪個語句句柄,服務(wù)器端會根據(jù)內(nèi)部記錄的句柄所對應(yīng)的已 經(jīng)編譯完成的SQL語句的執(zhí)行計劃,進(jìn)行語句執(zhí)行。數(shù)據(jù)庫審計要完成相應(yīng)的工作,需要執(zhí)行類似的過程,在系統(tǒng)的內(nèi)部也維護(hù)這樣的映射關(guān)系;同時由于大多數(shù) 數(shù)據(jù)庫的句柄,是在會話級的,句柄是可重用的,因此在數(shù)據(jù)庫審計中還要有效地維護(hù)句柄與session的關(guān)聯(lián),以及句柄的消亡。
在句柄維護(hù)之外,另一個有挑戰(zhàn)的工作就是參數(shù)的還原。參數(shù)的還原,首要的是要明確參數(shù)所對應(yīng)的句柄;在調(diào)用pStmt.setInt(1, ’22XXXXX5399’)時,在網(wǎng)絡(luò)中發(fā)送的包,會標(biāo)明這個參數(shù)是針對哪個句柄的,是針對第幾個參數(shù)的。作為數(shù)據(jù)庫審計產(chǎn)品,需要將參數(shù)與語句進(jìn)行映 射;更重要地要準(zhǔn)確地填回參數(shù)所在的位置,上面這個例子由于只有一個參數(shù),沒有什么挑戰(zhàn)性。但參數(shù)的綁定情況遠(yuǎn)比這個復(fù)雜,下面我們將提供一些例子來考驗 相關(guān)的數(shù)據(jù)庫審計產(chǎn)品。
用例1:一個基本的示例
String sql = "select PID,NAME from PERFORMANCE_C t where pid=:1 and balance>:2 and persionid>:3 and datefield>TO_DATE(:4,'YYYY-MM-DD')";
PreparedStatement pStmt_2 = testConn.getConnection().prepareStatement(sql);
pStmt_2.setInt(1, 84);
pStmt_2.setInt(2, 5555);
pStmt_2.setString(3, "120");
pStmt_2.setString(4, "1900-1-1");
pStmt_2.execute();
在這個示例中關(guān)鍵是看審計產(chǎn)品是否能將語句審計為:
select PID,NAME from PERFORMANCE_C t where pid=84 and balance>5555 and persionid>'120' and datefield>TO_DATE('1900-1-1','YYYY-MM-DD')
用例2:這是一個略有挑戰(zhàn)的例子,但這個挑戰(zhàn)已經(jīng)有數(shù)據(jù)庫審計產(chǎn)品無法將參數(shù)還原了
String sql = "select PID,NAME from PERFORMANCE_C t where pid=:pid and balance>:balance and personid>:personid and datefield>TO_DATE(:datefield,'YYYY-MM-DD')";
PreparedStatement pStmt_2 = testConn.getConnection().prepareStatement(sql);
pStmt_2.setInt(1, 84);
pStmt_2.setInt(2, 5555);
pStmt_2.setString(3, "120");
pStmt_2.setString(4, "1900-1-1");
pStmt_2.execute();
這里的挑戰(zhàn)在于參數(shù)并未使用常規(guī)的‘?’號或者‘:1’,這樣的方式,而是采用了字符串的方式;驗證的答案依然是:
select PID,NAME from PERFORMANCE_C t where pid=84 and balance>5555 and personid>'120' and datefield>TO_DATE('1900-1-1','YYYY-MM-DD')
用例3:這個挑戰(zhàn)同樣有數(shù)據(jù)庫審計產(chǎn)品出錯了
String sql = "select PID,NAME from PERFORMANCE_C t where personid>:1 and pid=:2 and balance>:3 and personid< 32 + :1;
PreparedStatement pStmt_2 = testConn.getConnection().prepareStatement(sql);
pStmt_2.setInt(1, "120");
pStmt_2.setInt(2, 84);
pStmt_2.setInt(3, 5555);
pStmt_2.setInt(4,84);
pStmt_2.execute();
這里的挑戰(zhàn)在于參數(shù)1和參數(shù)4用的是同參數(shù)編號;驗證的答案是:
select PID,NAME from PERFORMANCE_C t where personid>120 and pid=84 and balance>5555 and personid < 32 + 120;
用例4:若單純地采用’:’進(jìn)行分割的參數(shù)還原肯定會出錯
String sql = "select PID,NAME from PERFORMANCE_C t where pid>:1 and personid =:2 and content like ‘Name:3%’ and balance>:3";
PreparedStatement pStmt_2 = testConn.getConnection().prepareStatement(sql);
pStmt_2.setInt(1, 84);
pStmt_2.setString (2, "120");
pStmt_2.setString (3, 5555);
pStmt_2.execute();
驗證的答案是:
select PID,NAME from PERFORMANCE_C t where personid>120 and pid=84 and content like ‘’Name:3%’ and balance>5555;
用例5:這個挑戰(zhàn)是驗證對于多prepared語句下是否能準(zhǔn)確地將執(zhí)行的語句和對應(yīng)的參數(shù)都審計下來,這里我們將提供兩條語句進(jìn)行交互的執(zhí)行:
public void TestCase_PrepareSelect_3()
{
String sql_1 = "select pid,balance,account from F10_USER t where pid=:pid";
//String sql_2 = "select NAME,PID,balance from PERFORMANCE_C t where pid=:1 and
balance>:2 and persionid>:3 and datefield>TO_DATE(:4,'YYYY-MM-DD')";
String sql_2 = "select NAME,PID,balance from F10_USER t where pid=:1 andbalance>:3 and persionid>:2 and datefield>TO_DATE(:4,'YYYY-MM-DD')";
try {
pStmt = testConn.getConnection().prepareStatement(sql_1);
pStmt_2 = testConn.getConnection().prepareStatement(sql_2);
//第一輪執(zhí)行
pStmt_2.setInt(1, 94);
pStmt_2.setInt(2, 5555);
pStmt_2.setString(3, "120");
pStmt_2.setString(4, "1900-1-1");
pStmt_2.execute();
testRest_2 = pStmt_2.getResultSet();
while(testRest_2.next())
{
pid = testRest_2.getInt("PID");
System.out.println("PID="+pid);
}
pStmt.setInt(1, 84);
pStmt.execute();
testRest = pStmt.getResultSet();
while(testRest.next())
{
pid = testRest.getInt("PID");
System.out.println("PID="+pid);
}
//第2輪執(zhí)行
pStmt.setInt(1, 85);
pStmt.execute();
testRest = pStmt.getResultSet();
while(testRest.next())
{
pid = testRest.getInt("PID");
System.out.println("PID="+pid);
}
pStmt_2.setInt(1, 95);
pStmt_2.setInt(2, 1555);
pStmt_2.setString(3, "1305");
pStmt_2.setString(4, "1920-1-1");
pStmt_2.execute();
testRest_2 = pStmt_2.getResultSet();
while(testRest_2.next())
{
pid = testRest_2.getInt("PID");
System.out.println("PID="+pid);
}
//第3輪執(zhí)行
pStmt_2.setInt(1, 96);
pStmt_2.setInt(2, 5123);
pStmt_2.setString(3, "1201");
pStmt_2.setString(4, "1930-1-1");
pStmt_2.execute();
testRest_2 = pStmt_2.getResultSet();
while(testRest_2.next())
{
pid = testRest_2.getInt("PID");
System.out.println("PID="+pid);
}
pStmt.setInt(1, 86);
pStmt.execute();
testRest = pStmt.getResultSet();
while(testRest.next())
{
pid = testRest.getInt("PID");
System.out.println("PID="+pid);
}
// testRest.close();
// pStmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
應(yīng)該來講,這是一個并不復(fù)雜的多語句執(zhí)行示例,但某知名廠商的數(shù)據(jù)庫審計產(chǎn)品,將sql_1被審計到了4次,sql_2被審計到了1次。這僅僅是一種簡單的多語句執(zhí)行情況,我們現(xiàn)實中的應(yīng)用要更為復(fù)雜。
大家可以訪問這個鏈接,該鏈接將提供更為一個完整的參數(shù)綁定示例;我們可以測測所使用的數(shù)據(jù)庫審計產(chǎn)品能否準(zhǔn)確審計:http://m.wallpapic-fi.com/a/shujukuanquan/shujukufanghushouduan/shujukushenj/index.html
下面我們不再對這些簡單的情況進(jìn)行討論,我們將討論一種更為復(fù)雜的參數(shù)綁定情況,也就是批量參數(shù)綁定。
批量參數(shù)綁定是DBMS提供商為了提供更為高效的數(shù)據(jù)操作速度,而提供的一批參數(shù)發(fā)送,語句多次執(zhí)行;該方式通過更少的網(wǎng)絡(luò)通訊次數(shù)、更少的SQL 層的語境切換等大幅度提高編程效率。這種執(zhí)行方式在一般的編程中很少使用,但在類似于電信運(yùn)營商的計費(fèi)系統(tǒng)這樣的環(huán)境,對性能的要求很高,往往需要每秒接 近上萬條的插入或更新,則是一種較為常見的使用方式;因此對于運(yùn)行商、證券金融、互聯(lián)網(wǎng)這樣的瞬時交易量非常大,需要采用最為高效的數(shù)據(jù)處理方式的應(yīng)用環(huán) 境需要對這種參數(shù)綁定方式兼容。
下面是一個批量插入的示例,僅僅是為了檢驗審計產(chǎn)品是否支持該功能:
PreparedStatement statement = connection.prepareStatement("INSERT INTO Tbl1 VALUES(?, ?)");
//記錄1
statement.setInt(1, 1);
statement.setString(2, "Cujo");
statement.addBatch();
//記錄2
statement.setInt(1, 2);
statement.setString(2, "Fred");
statement.addBatch();
//記錄3
statement.setInt(1, 3);
statement.setString(2, "Mark");
statement.addBatch();
//批量執(zhí)行上面3條語句.
int [] counts = statement.executeBatch();
現(xiàn)在我們需要考察所使用的數(shù)據(jù)庫審計產(chǎn)品,能否準(zhǔn)確地審計下3條插入語句并且每條語句都能準(zhǔn)確地匹配上相應(yīng)的參數(shù);或者審計為1條插入語句,3組參數(shù)。
這只是批量插入中一個輕微的挑戰(zhàn),更為復(fù)雜的是當(dāng)批量更為大情況,Oracle和SQL Server這樣的考慮更為精細(xì)的產(chǎn)品,會對數(shù)據(jù)采用壓縮的處理方式,比如對重復(fù)值的表達(dá),對Null值的表達(dá)等;數(shù)據(jù)庫審計產(chǎn)品是否能有效地還原每個批量成員的值,將會是一個挑戰(zhàn)。
當(dāng)前我們所見到的絕大多數(shù)數(shù)據(jù)庫審計產(chǎn)品在批量參數(shù)綁定的執(zhí)行語句的情況下,都無法準(zhǔn)確審計下批量查詢中所執(zhí)行的語句和對應(yīng)的參數(shù)。