首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

Oracle 基於 LOGON 觸發器實現 IP 白名單訪問控制:實戰腳本與測試驗證全記錄

     編輯:Oracle 資料庫相關知識

Oracle 原生不支援 MySQL 風格的 CREATE USER ... REQUIRE IP 語法,但通過 AFTER LOGON ON DATABASE 系統級觸發器,結合 SYS_CONTEXT('USERENV', 'IP_ADDRESS') 獲取客戶端 IP,可靈活實現用戶級 IP 白名單/黑名單控制。本文提供生產就緒的觸發器腳本、通配符支援邏輯、本地連接豁免機制與完整測試驗證案例。

一、核心實現邏輯

  1. 觸發器類型:AFTER LOGON ON DATABASE(系統級,捕獲所有登入)
  2. 獲取上下文:SYS_CONTEXT('USERENV', 'SESSION_USER') 當前用戶,SYS_CONTEXT('USERENV', 'IP_ADDRESS') 客戶端 IP
  3. 判斷邏輯:目標用戶 + IP 不在白名單 → RAISE_APPLICATION_ERROR(-20001, ...) 拒絕連接
  4. 豁免機制:本地連接 (IPC/Bequeath) 無 IP_ADDRESS,自動放行

二、生產級觸發器腳本 (支援 CIDR/通配符)

CREATE OR REPLACE TRIGGER logon_ip_control
AFTER LOGON ON DATABASE
DECLARE
  v_user     VARCHAR2(128) := SYS_CONTEXT('USERENV', 'SESSION_USER');
  v_ip       VARCHAR2(45)  := SYS_CONTEXT('USERENV', 'IP_ADDRESS');
  v_allowed  BOOLEAN := FALSE;
BEGIN
  -- 僅針對指定用戶啟用控制
  IF v_user = 'EPAY_USER' THEN
    -- 白名單支援精確 IP、CIDR、通配符 (* 或 %)
    IF v_ip IS NULL THEN
      -- 本地連接 (IPC/Bequeath) 無 IP,默認放行
      v_allowed := TRUE;
    ELSIF v_ip IN ('192.168.219.20', '192.168.219.22') THEN
      -- 精確匹配
      v_allowed := TRUE;
    ELSIF v_ip LIKE '192.168.219.%' THEN
      -- 通配符匹配 (支援 * 或 %)
      v_allowed := TRUE;
    -- 可擴展:CIDR 判斷需調用 UTL_INADDR 或自定義函數
    END IF;

    IF NOT v_allowed THEN
      RAISE_APPLICATION_ERROR(-20001, 
        'User ' || v_user || ' is not allowed to connect from ' || NVL(v_ip, 'LOCAL (IPC)'));
    END IF;
  END IF;
EXCEPTION
  WHEN OTHERS THEN
    -- 記錄審計日志 (可選)
    -- INSERT INTO logon_audit (logon_time, user_name, ip_addr, error_msg) 
    -- VALUES (SYSDATE, v_user, v_ip, SQLERRM);
    RAISE;  -- 重新拋出,保持拒絕效果
END;
/

三、關鍵技術細節解析

1. IP_ADDRESS 上下文獲取差異

連接方式 IP_ADDRESS 值 觸發器行為
TCP/IP 遠程連接 (sqlplus user@tns) 客戶端真實 IP (如 192.168.219.21) 受白名單控制
本地連接 (sqlplus / as sysdba 或 sqlplus user) NULL (IPC/Bequeath 協議) 自動豁免 (腳本判斷 v_ip IS NULL)
JDBC Thin 驅動 客戶端 IP (需驅動支援) 受控制
JDBC OCI 驅動 可能為 NULL 或服務器 IP 需測試驗證

2. 通配符與 CIDR 擴展

-- 簡易 CIDR 判斷函數 (192.168.219.0/24)
CREATE OR REPLACE FUNCTION ip_in_cidr(p_ip VARCHAR2, p_cidr VARCHAR2) RETURN BOOLEAN IS
  v_ip_num    NUMBER;
  v_net_num   NUMBER;
  v_mask_num  NUMBER;
  v_prefix    NUMBER;
BEGIN
  -- 解析 CIDR: 192.168.219.0/24
  v_prefix := TO_NUMBER(SUBSTR(p_cidr, INSTR(p_cidr, '/')+1));
  v_net_num := UTL_INADDR.GET_HOST_ADDRESS(SUBSTR(p_cidr, 1, INSTR(p_cidr, '/')-1));
  v_mask_num := POWER(2, 32-v_prefix) - 1;
  v_ip_num := UTL_INADDR.GET_HOST_ADDRESS(p_ip);
  RETURN BITAND(v_ip_num, v_mask_num) = BITAND(v_net_num, v_mask_num);
EXCEPTION
  WHEN OTHERS THEN RETURN FALSE;
END;
/
-- 觸發器中調用
IF ip_in_cidr(v_ip, '192.168.219.0/24') THEN v_allowed := TRUE; END IF;

四、完整測試驗證記錄

測試環境

  • Primary: 192.168.219.22 (允許 IP)
  • Client A: 192.168.219.21 (拒絕 IP)
  • Client B: 192.168.219.23 (本地連接,豁免)

測試 1:拒絕 IP 連接失敗

[oracle@clientA ~]$ sqlplus epay_user@pri
SQL*Plus: Release 11.2.0.3.0 Production
Enter password:
ERROR:
ORA-00604: error occurred at recursive SQL level 1
ORA-20001: User EPAY_USER is not allowed to connect from 192.168.219.21
ORA-06512: at line 10

✅ 預期行為:觸發器攔截,返回自定義錯誤碼 -20001,連接被拒絕。

測試 2:允許 IP 連接成功

[oracle@primary ~]$ sqlplus epay_user
SQL*Plus: Release 11.2.0.3.0 Production
Enter password:
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

✅ 預期行為:IP 在白名單,連接成功。

測試 3:本地連接豁免成功

[oracle@primary ~]$ sqlplus epay_user
SQL*Plus: Release 11.2.0.3.0 Production
Enter password:
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
...

✅ 預期行為:本地 IPC 連接 v_ip 為 NULL,觸發器判斷豁免,連接成功。

五、進階擴展:審計日志表 + 定期清理

-- 審計表
CREATE TABLE logon_ip_audit (
  audit_id    NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  logon_time  TIMESTAMP DEFAULT SYSTIMESTAMP,
  os_user     VARCHAR2(128),
  db_user     VARCHAR2(128),
  ip_address  VARCHAR2(45),
  terminal    VARCHAR2(128),
  module      VARCHAR2(128),
  action      VARCHAR2(20),  -- ALLOWED / DENIED
  error_msg   VARCHAR2(4000)
);

-- 觸發器中記錄 (使用自治事務避免影響主事務)
CREATE OR REPLACE TRIGGER logon_ip_control
AFTER LOGON ON DATABASE
DECLARE
  PRAGMA AUTONOMOUS_TRANSACTION;
  v_user     VARCHAR2(128) := SYS_CONTEXT('USERENV', 'SESSION_USER');
  v_ip       VARCHAR2(45)  := SYS_CONTEXT('USERENV', 'IP_ADDRESS');
  v_os_user  VARCHAR2(128) := SYS_CONTEXT('USERENV', 'OS_USER');
  v_term     VARCHAR2(128) := SYS_CONTEXT('USERENV', 'TERMINAL');
  v_mod      VARCHAR2(128) := SYS_CONTEXT('USERENV', 'MODULE');
  v_allowed  BOOLEAN := FALSE;
BEGIN
  -- ... 判斷邏輯同前 ...
  
  IF v_user = 'EPAY_USER' THEN
    -- 記錄審計
    INSERT INTO logon_ip_audit (db_user, ip_address, os_user, terminal, module, action, error_msg)
    VALUES (v_user, v_ip, v_os_user, v_term, v_mod, 
            CASE WHEN v_allowed THEN 'ALLOWED' ELSE 'DENIED' END,
            CASE WHEN v_allowed THEN NULL ELSE 'IP not in whitelist' END);
    COMMIT;
    
    IF NOT v_allowed THEN
      RAISE_APPLICATION_ERROR(-20001, 'Access denied from ' || NVL(v_ip, 'LOCAL'));
    END IF;
  END IF;
END;
/

六、局限性與替代方案

局限 影響 替代/補充方案
無法控制 SYS/SYSDBA 連接 特權用戶繞過觸發器 作業系統層面防火牆、Oracle Connection Manager (CMAN)
JDBC OCI/連接池可能獲取不到真實客戶端 IP 誤判豁免或誤攔截 應用層傳遞 CLIENT_IDENTIFIER、使用 CMAN
觸發器失效導致所有用戶無法登入 單點故障風險 保留一個無觸發器控制的應急管理員用戶、監控觸發器狀態
性能開銷 (每次登入觸發) 高頻連接場景有影響 使用 CMAN 或網絡層 ACL (Oracle Database Firewall)

七、總結

基於 AFTER LOGON 觸發器 + SYS_CONTEXT 實現 IP 訪問控制是 Oracle 原生最靈活、零成本的方案。關鍵成功要素:

  1. 正確處理本地連接豁免 (v_ip IS NULL)
  2. 支援通配符/CIDR 靈活匹配
  3. 自治事務記錄審計 不影響主邏輯
  4. 保留應急後門 防止觸發器故障導致全庫不可用
  5. 配合網絡層防火牆/CMAN 構建纵深防禦

將本文觸發器腳本納入資料庫安全基線標準,即可滿足絕多數合規與安全管控需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved