從Java層防禦延伸到資料庫層:PL/SQL動態SQL注入風險與修復方案
企業常重視應用層PreparedStatement防注入,卻忽略PL/SQL動態SQL(EXECUTE IMMEDIATE)同樣存在注入風險。本文透過實例演示攻擊向量與DBMS_ASSERT修復全過程。
一、脆弱程式碼範例
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;
/
二、三大注入向量分析
| 參數 | 攻擊載荷範例 | 造成影響 |
| p_table | t1; DROP TABLE users; -- | 執行任意DDL/DML |
| p_constraint_name | c1; GRANT DBA TO PUBLIC; -- | 權限提升 |
| p_schema(USER) | 惡意建立名為"SCOTT; DROP TABLE t"的用戶 | 間接注入 |
三、防禦策略優先序
- 靜態SQL:能不用動態SQL就不用
- 綁定變數:
EXECUTE IMMEDIATE 'SELECT * FROM t WHERE id=:b' USING v_id; — 但DDL不支援綁定
- 輸入驗證/清洗:
DBMS_ASSERT包 — DDL動態SQL唯一可行方案
四、DBMS_ASSERT核心函數評測
| 函數 | 功能 | 適用性評估 |
| SIMPLE_SQL_NAME | 驗證簡單SQL名稱(無特殊字元) | ❌ 資料字典讀出的名稱不含雙引號,驗證失敗 |
| QUALIFIED_SQL_NAME | 驗證完整限定名(schema.object) | ❌ 同左,雙引號問題 |
| SCHEMA_NAME | 驗證現有Schema名稱 | ⚠️ 二次攻擊風險:攻擊者預建惡意名稱物件 |
| SQL_OBJECT_NAME | 驗證現有物件名稱 | ⚠️ 同左,二次攻擊風險 |
| ENQUOTE_NAME | 雙引號包裹名稱,驗證合法性 | ✅ 最佳選擇:防注入+保留大小寫+無二次攻擊風險 |
| ENQUOTE_LITERAL | 單引號包裹字面值 | 適用字串字面值,非識別碼 |
五、修復後程式碼
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;
/
六、關鍵注意事項
- 大小寫敏感:
ENQUOTE_NAME(..., FALSE)保留原大小寫,呼叫端必須傳入正確大小寫(如Table_1而非TABLE_1)
AUTHID CURRENT_USER:以呼叫者權限執行,配合DBMS_ASSERT雙重防護
- 審計追蹤:建立
DDL觸發器記錄所有ALTER/DROP/GRANT操作
- 程式碼掃描:納入CI/CD靜態分析,標記所有
EXECUTE IMMEDIATE需人工審查
七、延伸防禦
- 最小權限:程式單元擁有者僅具備必要DDL權限
- 資料庫防火牆/統一審計:記錄所有動態SQL執行
- 定期滲透測試:含PL/SQL注入測試案例
- 上一篇文章: Oracle字串分割三種實作方案:自訂函數、PIPELINED、REGEXP_CONNECT BY對比
- 下一篇文章: Oracle分頁查詢存儲過程:動態多條件+雙層ROWNUM標準實作
Copyright ©
程式碼之家 All Rights Reserved