今天在指導一個小型DB2項目開發時,突然想到了爲什麼SQL注入攻擊之類的黑客技術在我接受的DB2的mainframe項目中根本沒有提及,應該就是因爲DB2的靜態SQL語句機制。
我們先來看定義。
靜態SQL:在編程語言中,語句以hard code的方式寫在程序中,但是其中允許有變量。這樣的程序需要經過DB2預編譯,將對這樣的SQL語句的調用變成native language call。而實際的SQL語句則會被BIND成爲DB2系統中的可執行包,與應用程序綁定,被其調用。
典型的靜態SQL:
EXEC SQL
UPDATE TESTTABLE SET
COL1 = :VALUE1
COL2 = :VALUE2
WHERE COL3 LIKE :VALUE3
END-EXEC.
其中,COL1, COL2, COL3是TABLE COLUMN名字,而VALUE1, VALUE2, VALUE3是應用程序的NATIVE VARIABLE。
目前,商業DB2應用程序採用的大多是靜態SQL,特點是一次BIND,多次穩定運行,省去了數據庫每次尋找ACCESS PATH的OVER HEAD,並且,如果環境遷移,只須重新BIND,而不需要重新編譯應用程序或該產品配置。
動態SQL:在編程語言中,語句以動態的VARIABLE的方式,提交給DB2運行。每次需要PREPARE STATEMENT FROM A VARIABLE,然後再EXECUTE。最常見的這類應用就是JDBC, ODBC這樣的調用。其缺點是,每次運行都需要數據庫決定ACCESS PATH,造成了很多OVERHEAD,但是同時如果表中數據信息時常變動,由於每次都做優化處理,有時也會增進性能。
SQL注入攻擊是指在提交查詢表單時,故意的輸入一些SQL保留字,以擾亂程序執行的SQL語句,甚至插入攻擊者指定的SQL語句執行。這樣的攻擊常見於ASP + MS SQL server,但並不侷限於此。
比如,某應用提供查詢數據庫中所有職員姓名信息的功能,要求使用者輸入姓名,比如SMITH,便查詢出所有姓名爲SMITH的職員的員工號。
假設,程序中構造sql的邏輯如下:
SQLSTMT = "SELECT EMP_ID FROM EMP_TABLE WHERE EMP_NAME LIKE /'" +
NAME_FROM_UI + "/'";
EXECUTE SQLSTMT;
則構造出的SQL如下:
SELECT EMP_ID
FROM EMP_TABLE
WHERE EMP_NAME LIKE 'SMITH';
(黃色的字符爲從UI接收的輸入,下同)
則查詢正確;
但是,攻擊者如果提交這樣的一個姓名:
SMITH'; UPDATE EMP_TABLE SET SALARY = 100000;
如果程序中沒有對SQL特殊字符'做轉義處理,則生成的SQL就是:
SELECT EMP_ID
FROM EMP_TABLE
WHERE EMP_NAME LIKE 'SMITH'; UPDATE EMP_TABLE SET SALARY = 100000; ';
這樣,假設表中真的有SALARY這個COLUMN,並且例子中處理從UI接受到的字串時沒有對'進行轉義處理,攻擊者就能將所有人的薪水改爲100000。
(實際上,SQL注入攻擊並不是簡單轉義'就能避免的,攻擊者可以直接使用&加'的UNICODE編碼來規避程序轉義等,在此不深入探討)
(SQL注入攻擊可以有多種方法獲取表名、列名,而不僅僅是全憑猜測,最常見的手法爲先注入描述表的SQL語句,獲取表信息後進行實際攻擊等等,在此不再詳述,有興趣的讀者可以自己GOOGLE一下。)
以上的攻擊,對於動態SQL語句來說,無疑是可以成功的,因爲每次執行,數據庫都會爲這個語句重新確定ACCESS PATH,所以,即使每次語句不同,其執行也不會跑出錯誤。
而如果是靜態SQL語句,由於其ACCESS PATH已經確定,換言之,在上例中就意味着,SELECT語句要使用的索引,優先APPLY過濾條件的順序之類,都已經確定的寫在DB2的系統表中了,這時,如果試圖使用SQL注入攻擊,DB2已經明確的知道了,要做的查詢是什麼,並且已經知道了,傳進來的變量僅用於過濾條件,這樣就杜絕了在查詢中注入攻擊者的SQL語句的可能。
筆者在毫無輸入轉義判定的DB2應用程序(靜態SQL語句)中測試了例中的注入攻擊,結果是DB2正確返回SQLCODE 100,提示找不到需要查詢的記錄。
由此筆者認爲,在使用靜態SQL語句的DB2應用程序中,應無SQL注入攻擊之虞。