以下的文章主要是淺談Oracle數據庫的安全:PL/SQL的SQL注入,我在一個信譽度很好的網站找到一個關於Oracle數據庫的安全:PL/SQL的SQL注入的資料,拿出來供大家分享。望大家會有所收獲。
SQL注入,一個老掉牙的安全問題,有SQL的地方就會有SQL注入。一般做企業應用的只關注Java層面的編寫規范,比如使用preparedStatement,或者干脆直接過濾掉危險字符等等。
其實在編寫PL/SQL的function或procedure的時候,也存在注入的問題,我們來簡單探討一下。
例如有這樣一個procedure,功能為禁用某個table的constraint:
Sql代碼
- CREATE OR REPLACE PROCEDURE Disable_Constraint
( p_constraint_name VARCHAR2, p_table VARCHAR2 )- AUTHID CURRENT_USER
- AS
- p_schema VARCHAR2(32) := USER;
- sql_stmt VARCHAR2(2000) := 'ALTER TABLE '
- || p_schema
- || '.'
- || p_table
- || ' DISABLE CONSTRAINT '
- || p_constraint_name ;
- BEGIN
- EXECUTE IMMEDIATE sql_stmt;
- END;
- /
了解SQL注入的同學應該可以看出來,上面的procedure存在幾個危險的變量:
1. p_table
2. p_constraint_name
3. p_schema
前兩者容易發現,但為什還有p_schema呢?因為當前的USER名字也有可能是用戶構造的危險字符串。好了,根據一般規律,我們應該遵循以下順序進行修改:
1. 靜態SQL。能不使用變量就不使用。
2. 綁定變量。與Java中的PreparedStatement類似,不把數據直接拼接在sql裡,而是存入變量中,直接被Oracle數據庫使用。
3. 檢查變量的值。
顯然,前兩個方法對這個procedure不適用。我們只能使用最不爽的第3個方法。 好在對於PL/SQL來說,我們不用自己編寫復雜的字符判斷。Oracle有個SYS.DBMS_ASSERT包,提供了一些預置的function,如下:
- NOOP No Operation. Returns string unchanged
- SIMPLE_SQL_NAME Verify that the input string is
a simple SQL name.- QUALIFIED_SQL_NAME Verify that the input string
is a qualifIEd SQL name.- SCHEMA_NAME This function verifIEs that the input
string is an existing schema name.- SQL_OBJECT_NAME This function verifIEs that the input
parameter string is a qualified SQL identifIEr of an
existing SQL object.- ENQUOTE_NAME This function encloses a name in double
quotes.- ENQUOTE_LITERAL Add leading and trailing single
quotes to a string literal.
在執行這些function時,如果傳入的變量不滿足規定的格式或條件,則會拋異常,從而保護我們自己的procedure不被SQL注入。
我們判斷這些方法是否可用:
1. SIMPLE_SQL_NAME, QUALIFIED_SQL_NAME
這些方法要求用戶出入的參數本身是一個有效的sql名字。比如,如果有個table名為"Table One",那麼就要求傳入的值中包含雙引號。使用這些方法存在一個問題,直接從data-dictionary讀取出來的table名字是不帶雙引號的。如果用戶直接從data-dictionary中讀取table名字,然後直接傳入我們的procedure,則會因為它不滿足simple sql name的要求而拋異常,但實際上這個table名字應該是正確的。所以不能直接使用這些function。
2. SCHEMA_NAME, SQL_OBJECT_NAME
這些方法要求傳入的參數值是數據庫中已經存在的對象名字。如果Oracle數據庫中本來有個table名為 "Table One",那麼如果用戶傳入Table One,則被視為正確。使用這些方法,避免了第一個方法的data-dictionary問題,而且也能夠避免遭受類似table' -- 的問題。但存在所謂二次攻擊的問題。如果用戶提前創建了一個包含危險字符的table,然後再調用我們的procedure,依舊會造成SQL注入。
3. ENQUOTE_NAME, ENQUOTE_LITERAL
這些方法直接把參數的值用雙引號或單引號括起來。如果括起來之後的值本身還存在危險的話,會拋異常。對於我們舉例的procedure來說,只需要使用ENQUOTE_NAME。ENQUOTE_NAME需要兩個參數,一個是需要enquote的變量,另一個為是否轉換為大寫。現在,對於我們的procedure,應該使用ENQUOTE_NAME(p_table, FALSE),保證Table One不被轉換為"TABLE ONE"。
這是我們的最終解決方案。但需要注意的是,由於使用了ENQUOTE_NAME,對於我們的procedure來說,table和constraint的名字對大小寫敏感。如果名為table_1,則必須傳入TABLE_1,否則會執行錯誤。
修改後的代碼如下:
Sql代碼
- CREATE OR REPLACE PROCEDURE Disable_Constraint
( p_constraint_name VARCHAR2, p_table VARCHAR2 )- AUTHID CURRENT_USER
- AS
- p_schema VARCHAR2(32) := SYS.DBMS_ASSERT.
ENQUOTE_NAME(USER, FALSE);- sql_stmt VARCHAR2(2000);
- safe_table VARCHAR2(32);
- safe_constraint VARCHAR2(32);
- BEGIN
- safe_table := SYS.DBMS_ASSERT.
ENQUOTE_NAME(p_table, FALSE);- safe_constraint := SYS.DBMS_ASSERT.
ENQUOTE_NAME(p_constraint_name, FALSE);- sql_stmt := 'ALTER TABLE '
- || p_schema
- || '.'
- || safe_table
- || ' DISABLE CONSTRAINT '
- || safe_constraint ;
- EXECUTE IMMEDIATE sql_stmt;
- END;
- /
上述的相關內容就是對Oracle數據庫的安全:PL/SQL的SQL注入的描述,希望會給你帶來一些幫助在此方面。