Oracle允許使用幾個PL/SQL API(UTL_TCP, UTL_SMTP, UTL_MAIL, UTL_HTTP和 UTL_INADDR)訪問外部網絡服務,這些API都使用TCP協議,在上一個數據庫版本中(10g)是通過一個基於用戶是否被授予執行某個包的許可的 on/off開關來實現的,Oracle 11g引入了細粒度訪問網絡服務,
通過在XML DB 數據庫中使用訪問控制列表(ACL)來實現,允許控制哪個用戶能夠訪問哪個網絡資源,而不關心包的授權。
使用FTP或WebDav可以直接在XML DB 數據庫中創建、修改和刪除訪問控制列表,Oracle提供了DBMS_NETWORK_ACL_ADMIN和 DBMS_NETWORK_ACL_UTILITY程序包允許從PL/SQL管理訪問控制列表,這些API就是本文的主角。
創建一個訪問控制列表(ACL)
訪問控制列表是使用DBMS_NETWORK_ACL_ADMIN程序包來操作的,CREATE_ACL存儲過程使用下面的參數來創建一個新的訪問控制列表:
? acl - 訪問控制列表XML文件的名字,產生在XML DB 數據庫中的/sys/acls目錄下? description - 訪問控制列表的描述信息? principal - 第一個被授予或拒絕的用戶賬號,大小寫敏感? is_grant - TRUE意味着授予了權限,FALSE意味着權限被拒絕? privilege - 給UTL_TCP, UTL_SMTP, UTL_MAIL和UTL_HTTP授予connect權限,給UTL_INADDR名稱/ip解析授予resolve權限,大小寫敏感? start_date - 默認值是NULL,當指定了一個值後,訪問控制列表只有在指定的日期到達時或到達後才被激活? en_date - 訪問控制列表結束日期(可選的)
下面的代碼創建了兩個測試用戶充當委託人,然後又創建了一個新的訪問控制列表。
CONN sys/password@db11g AS SYSDBA
CREATE USER test1 IDENTIFIED BY test1;
GRANT CONNECT TO test1;
CREATE USER test2 IDENTIFIED BY test2;
GRANT CONNECT TO test2;
BEGIN
DBMS_NETWORK_ACL_ADMIN.create_acl (
acl => 'test_acl_file.xml',
description => 'A test of the ACL functionality',
principal => 'TEST1',
is_grant => TRUE,
privilege => 'connect',
start_date => SYSTIMESTAMP,
end_date => NULL);
COMMIT;
END;
/
一旦創建完畢,訪問控制列表就能夠在http://host:port/sys/acls目錄下看到。
使用ADD_PRIVILEGE存儲過程將其他的用戶或角色添加到訪問控制列表中,它的參數與CREATE_ACL存儲過程的參數類似,省略了DESCRIPTION參數,同時增加了POSITION參數,它用於設置優先順序。
BEGIN
DBMS_NETWORK_ACL_ADMIN.add_privilege (
acl => 'test_acl_file.xml',
principal => 'TEST2',
is_grant => FALSE,
privilege => 'connect',
position => NULL,
start_date => NULL,
end_date => NULL);
COMMIT;
END;
/
每個委託人在訪問控制列表中都被作爲一個獨立的訪問控制單元(ACE)進行定義,當定義了多條原則時,他們按照從上到下的順序被評估,直到最後一條定義 權限的原則,這就意味着一個拒絕訪問某個資源的角色可以被授予一個用戶,但是如果這個用戶又作爲一個委託人定義在文件中時,這個定義將覆蓋角色的定義,使 用POSITION參數保證權限是按順序進行評估的。
使用DELETE_PRIVILEGE存儲過程移除權限,如果IS_GRANT或PRIVILEGE參數的值是NULL,將移除所有授予的權限。
BEGIN
DBMS_NETWORK_ACL_ADMIN.delete_privilege (
acl => 'test_acl_file.xml',
principal => 'TEST2',
is_grant => FALSE,
privilege => 'connect');
COMMIT;
END;
/
使用DROP_ACL刪除訪問控制列表
BEGIN
DBMS_NETWORK_ACL_ADMIN.drop_acl (
acl => 'test_acl_file.xml');
COMMIT;
END;
/ 轉貼 給網絡分配一個訪問控制列表
使用ASSIGN_ACL存儲過程給網絡分配訪問控制列表,它有下面一些參數:
? acl - 訪問控制列表XML文件的名字? host - 主機名,域名,ip地址或分配的子網,主機名大小寫敏感,ip地址和域名允許使用通配符? lower_port - 默認值是NULL,爲connect權限指定低端口範圍? upper_port - 默認值是NULL,如果指定了lower_port,同時upper_port的值爲 NULL,它就認爲upper_port等同於lower_port
下面的代碼展示了前面創建的訪問控制列表被分配一個特定的ip地址和一個子網。
BEGIN
DBMS_NETWORK_ACL_ADMIN.assign_acl (
acl => 'test_acl_file.xml',
host => '192.168.2.3',
lower_port => 80,
upper_port => NULL);
DBMS_NETWORK_ACL_ADMIN.assign_acl (
acl => 'test_acl_file.xml',
host => '10.1.10.*',
lower_port => NULL,
upper_port => NULL);
COMMIT;
END;
/
只有一個訪問控制列表可以分配給一個特殊的主機+端口範圍的組合,給主機+端口範圍分配一個新的訪問控制列表將導致前面分配的訪問控制列表被刪除,在你 開始一個新的分配操作時要特別留意前一個訪問控制列表關閉的端口現在又被你打開了,因此分配給192.168.2.3:80的訪問控制列表的優先級比分配 給192.168.2.*的訪問控制列表要高。
UNASSIGN_ACL存儲過程允許你手動刪除訪問控制列表,它使用的參數與ASSIGN_ACL存儲過程相同,使用NULL參數作爲通配符。
BEGIN
DBMS_NETWORK_ACL_ADMIN.unassign_acl (
acl => 'test_acl_file.xml',
host => '192.168.2.3',
lower_port => 80,
upper_port => NULL);
COMMIT;
END;
/
訪問控制列表視圖
DBA_NETWORK_ACLS, DBA_NETWORK_ACL_PRIVILEGES和USER_NETWORK_ACL_PRIVILEGES視圖顯示當前的訪問控制列表設置,下面預期的輸出認爲沒有執行過delete/drop/unassign操作。
DBA_NETWORK_ACLS視圖顯示關於網絡和訪問控制列表分配的信息。
COLUMN host FORMAT A30
COLUMN acl FORMAT A30
SELECT host, lower_port, upper_port, acl
FROM dba_network_acls;
HOST
------------------------------
10.1.10.*
192.168.2.3
LOWER_PORT UPPER_PORT ACL---------- ---------- ------------------------------ /sys/acls/test_acl_file.xml
80 80 /sys/acls/test_acl_file.xml
2 rows selected.
SQL>
DBA_NETWORK_ACL_PRIVILEGES視圖顯示關於與訪問控制列表聯合的權限信息。
COLUMN acl FORMAT A30
COLUMN principal FORMAT A30
SELECT acl,
principal,
privilege,
is_grant,
TO_CHAR(start_date, 'DD-MON-YYYY') AS start_date,
TO_CHAR(end_date, 'DD-MON-YYYY') AS end_date
FROM dba_network_acl_privileges;
ACL PRINCIPAL
------------------------------ ------------------------------
/sys/acls/test_acl_file.xml TEST1
/sys/acls/test_acl_file.xml TEST2
PRIVILE IS_GR START_DATE END_DATE------- ----- ----------- -----------connect true 02-APR-2008connect false2 rows selected.
SQL>
USER_NETWORK_ACL_PRIVILEGES視圖顯示當前用戶網絡訪問控制列表設置。
CONN test1/test1@db11g
COLUMN host FORMAT A30
SELECT host, lower_port, upper_port, privilege, status
FROM user_network_acl_privileges;
HOST LOWER_PORT UPPER_PORT PRIVILE STATUS
------------------------------ ---------- ---------- ------- -------
10.1.10.* connect GRANTED
192.168.2.3 80 80 connect GRANTED
2 rows selected.
SQL>
CONN test2/test2@db11g
COLUMN host FORMAT A30
SELECT host, lower_port, upper_port, privilege, status
FROM user_network_acl_privileges;
HOST LOWER_PORT UPPER_PORT PRIVILE STATUS
------------------------------ ---------- ---------- ------- -------
10.1.10.* connect DENIED
192.168.2.3 80 80 connect DENIED
2 rows selected.
SQL> 轉權限檢查
處理訪問控制列表視圖外,還可以使用DBMS_NETWORK_ACL_ADMIN包中的CHECK_PRIVILEGE和CHECK_PRIVILEGE_ACLID函數來檢查權限。
CONN sys/password@db11g AS SYSDBA
SELECT DECODE(
DBMS_NETWORK_ACL_ADMIN.check_privilege('test_acl_file.xml', 'TEST1', 'connect'),
1, 'GRANTED', 0, 'DENIED', NULL) privilege
FROM dual;
PRIVILE
-------
GRANTED
1 row selected.
SQL>
COLUMN acl FORMAT A30
COLUMN host FORMAT A30
SELECT acl,
host,
DECODE(
DBMS_NETWORK_ACL_ADMIN.check_privilege_aclid(aclid, 'TEST2', 'connect'),
1, 'GRANTED', 0, 'DENIED', NULL) privilege
FROM dba_network_acls;
PRIVILE
-------
DENIED
1 row selected.
SQL>
DBMS_NETWORK_ACL_UTILITY包包括了幫助判斷可能匹配的域的函數,DOMAINS表函數按順序返回所有可能受影響的主機,域,ip地址或子網的集合。
SELECT *
FROM TABLE(DBMS_NETWORK_ACL_UTILITY.domains('oel5-11g.localdomain'));
COLUMN_VALUE
-------------------------------
oel5-11g.localdomain
*.localdomain
*
3 rows selected.
SQL>
SELECT *
FROM TABLE(DBMS_NETWORK_ACL_UTILITY.domains('192.168.2.3'));
COLUMN_VALUE
-------------------------------
192.168.2.3
192.168.2.*
192.168.*
192.*
*
5 rows selected.
SQL>
DOMAIN_LEVEL函數返回主機,域,ip地址或子網的級數。
SELECT DBMS_NETWORK_ACL_UTILITY.domain_level('oel5-11g.localdomain')
FROM dual;
DBMS_NETWORK_ACL_UTILITY.DOMAIN_LEVEL('OEL5-11G.LOCALDOMAIN')
-------------------------------------------------------------
2
1 row selected.
SQL>
SELECT DBMS_NETWORK_ACL_UTILITY.domain_level('192.168.2.3')
FROM dual;
DBMS_NETWORK_ACL_UTILITY.DOMAIN_LEVEL('192.168.2.3')
----------------------------------------------------
4
1 row selected.
SQL>
在爲可能匹配的主機,域,ip地址或子網查詢訪問控制列表視圖是這些函數可能非常有用。
SELECT host,
lower_port,
upper_port,
acl,
DECODE(
DBMS_NETWORK_ACL_ADMIN.check_privilege_aclid(aclid, 'TEST1', 'connect'),
1, 'GRANTED', 0, 'DENIED', null) PRIVILEGE
FROM dba_network_acls
WHERE host IN (SELECT *
FROM TABLE(DBMS_NETWORK_ACL_UTILITY.domains('10.1.10.191')))
ORDER BY
DBMS_NETWORK_ACL_UTILITY.domain_level(host) desc, lower_port, upper_port;
HOST LOWER_PORT UPPER_PORT ACL PRIVILE
------------------------------ ---------- ---------- ------------------------------ -------
10.1.10.* /sys/acls/test_acl_file.xml GRANTED
1 row selected.
SQL> 測試訪問控制列表
用戶TEST1和TEST2分別擁有了允許的和拒絕的訪問控制列表,這就意味着我們可以開始通過對比對訪問外部網絡服務時它們的響應來測試訪問控制列表的功能,下面的代碼授予了這兩個用戶都可以執行UTL_HTTP包的權限,然後嘗試從每個用戶訪問一個web頁面。
CONN sys/password@db11g AS SYSDBA
GRANT EXECUTE ON UTL_HTTP TO test1, test2;
CONN test1/test1@db11g
DECLARE
l_url VARCHAR2(50) := 'http://192.168.2.3:80';
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
BEGIN
-- Make a HTTP request and get the response.
l_http_request := UTL_HTTP.begin_request(l_url);
l_http_response := UTL_HTTP.get_response(l_http_request);
UTL_HTTP.end_response(l_http_response);
END;
/
PL/SQL procedure successfully completed.
SQL>
CONN test2/test2@db11g
DECLARE
l_url VARCHAR2(50) := 'http://192.168.2.3:80';
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
BEGIN
-- Make a HTTP request and get the response.
l_http_request := UTL_HTTP.begin_request(l_url);
l_http_response := UTL_HTTP.get_response(l_http_request);
UTL_HTTP.end_response(l_http_response);
END;
/
DECLARE
*
ERROR at line 1:
ORA-29273: HTTP request failed
ORA-06512: at "SYS.UTL_HTTP", line 1029
ORA-24247: network access denied by access control list (ACL)
ORA-06512: at line 7
SQL>
從返回的信息我們不難看出用戶TEST1能夠訪問web頁面,而用戶TEST2被訪問控制列表拒絕了,服務器的默認行爲是拒絕訪問外部網絡服務,下面顯示了一個新用戶的測試情況。
CONN sys/password@db11g AS SYSDBA
CREATE USER test3 IDENTIFIED BY test3;
GRANT CONNECT TO test3;
GRANT EXECUTE ON UTL_HTTP TO test3;
CONN test3/test3@db11g
DECLARE
l_url VARCHAR2(50) := 'http://192.168.2.3:80';
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
BEGIN
-- Make a HTTP request and get the response.
l_http_request := UTL_HTTP.begin_request(l_url);
l_http_response := UTL_HTTP.get_response(l_http_request);
UTL_HTTP.end_response(l_http_response);
END;
/
DECLARE
*
ERROR at line 1:
ORA-29273: HTTP request failed
ORA-06512: at "SYS.UTL_HTTP", line 1029
ORA-24247: network access denied by access control list (ACL)
ORA-06512: at line 7
SQL>
在從10g升級到11g時,訪問外部網絡服務時可能會產生一些混亂,在那種情況下,你需要實現合理的訪問控制列表。
其他安全因素
Pete Finnigan在它的博客上和關於訪問控制列表的安全陳述只沒有附上具體的程序包,這就意味着通過UTL_TCP, UTL_SMTP, UTL_MAIL和UTL_HTTP加上connect權限就能在服務器上打開一個端口。牢記這一點並考慮以下事項:
◆細粒度訪問網絡服務的使用不能作爲忽略基本的安全評估的藉口,如收回與網絡服務有關程序包的不必要的權限。
◆通過限制對特定端口的訪問控制你的服務是可用的,如果你僅僅需要訪問http 80端口,指定這個端口比在服務器上開放所有端口的訪問要好得多。
◆授權時使用通配符比不使用通配符安全性更差,也更危險。
◆你必須保護你的訪問控制列表,如果有人能夠修改它們,因爲保護機制問題它們變得毫無用處,阻止直接訪問存儲在XML DB 數據庫中的訪問控制列表,確保用戶不能訪問管理API.