Public的特殊作用
在前面我们提到了用户权限与角色,而在数据库中,有一个特殊的权限集合,那就是PUBLIC。
PUBLIC是公共的含义,是数据库中一个特殊的“角色”,授予PUBLIC的权限会自动被所有用户继承,所以认真审核PUBLIC所拥有的权限是提高数据库安全的必要工作之一。
在catalog.sql脚本中,可以看到大量视图等对象在创建之后,就将权限授予了PUBLIC:
catalog.sql:grant select on tab to public with grant option; catalog.sql:grant select on col to public with grant option; catalog.sql:grant select on syssegobj to public with grant option; catalog.sql:grant select on tabquotas to public with grant option; catalog.sql:grant select on sysfiles to public with grant option; catalog.sql:grant select on synonyms to public with grant option; catalog.sql:grant select on publicsyn to public with grant option; catalog.sql:grant select on GLOBAL_NAME to public with grant option
同样大量过程对象的执行权限也被授予:
catmeta.sql:grant execute on ku$_histgrm_t to public catmeta.sql:grant execute on ku$_histgrm_list_t to public catmeta.sql:grant execute on ku$_col_stats_t to public catmeta.sql:grant execute on ku$_col_stats_list_t to public catmeta.sql:grant execute on ku$_10_1_col_stats_t to public catmeta.sql:grant execute on ku$_10_1_col_stats_list_t to public catmeta.sql:grant execute on ku$_cached_stats_t to public catmeta.sql:grant execute on ku$_tab_ptab_stats_t to public
在DBA_TAB_PRIVS中,PUBLIC具有的对象权限就多达2000多个:
SQL> select count(*) from dba_tab_privs where grantee='PUBLIC'; COUNT(*) ---------- 2006
从安全角度着眼,这其中的很多权限不需授予给PUBLIC,具体的用户需要这些权限时可以进行单独授予:
SQL> SELECT table_name,grantee,privilege 2 FROM dba_tab_privs WHERE grantee='PUBLIC' AND table_name LIKE 'UTL%'; TABLE_NAME GRANTEE PRIVILEGE -------------------- -------------------- -------------------- UTL_ALL_IND_COMPS PUBLIC SELECT UTL_MATCH PUBLIC EXECUTE UTL_COLL PUBLIC EXECUTE UTL_REF PUBLIC EXECUTE UTL_NLA PUBLIC EXECUTE UTL_NLA_ARRAY_INT PUBLIC EXECUTE UTL_NLA_ARRAY_FLT PUBLIC EXECUTE UTL_NLA_ARRAY_DBL PUBLIC EXECUTE UTL_LMS PUBLIC EXECUTE UTL_I18N PUBLIC EXECUTE UTL_COMPRESS PUBLIC EXECUTE UTL_GDK PUBLIC EXECUTE UTL_ENCODE PUBLIC EXECUTE UTL_URL PUBLIC EXECUTE UTL_HTTP PUBLIC EXECUTE UTL_SMTP PUBLIC EXECUTE UTL_INADDR PUBLIC EXECUTE UTL_TCP PUBLIC EXECUTE UTL_RAW PUBLIC EXECUTE UTL_FILE PUBLIC EXECUTE
通常建议取消UTL_SMTP,UTL_TCP,UTL_HTTP,UTL_FILE等对象对于PUBLIC的授权,但是注意,取消授权之后,会导致部分系统对象失效,需要重新编译,所以建议,在进行任何权限回收时,必须经过认真的测试:
SQL> select count(*) from dba_objects where STATUS='INVALID'; COUNT(*) ---------- 0 SQL> REVOKE EXECUTE ON dbms_job FROM PUBLIC; Revoke succeeded. SQL> REVOKE EXECUTE ON utl_tcp FROM public; Revoke succeeded. SQL> REVOKE EXECUTE ON utl_smtp FROM public; Revoke succeeded. SQL> REVOKE EXECUTE ON utl_http FROM public; Revoke succeeded. SQL> REVOKE EXECUTE ON utl_inaddr FROM public; Revoke succeeded. SQL> REVOKE EXECUTE ON utl_file FROM public; Revoke succeeded. SQL> select count(*) from dba_objects where STATUS='INVALID'; COUNT(*) ---------- 65 SQL> @?/rdbms/admin/utlrp SQL> select count(*) from dba_objects where STATUS='INVALID'; COUNT(*) ---------- 4 SQL> col OBJECT_NAME for a30 SQL> select OWNER,OBJECT_NAME from dba_objects where STATUS='INVALID'; OWNER OBJECT_NAME ------------------------------ ------------------------------ DBSNMP BSLN_INTERNAL XDB DBMS_XMLPARSER XDB DBMS_XSLPROCESSOR XDB DBMS_XDBT
最后几个对象,如果不使用XMLDB则可以忽略,否则可以直接向相关用户进行授权。
认真审核PUBLIC角色的权限是数据库安全管理的重要工作。
过度授权的风险
从安全角度考虑,数据库的授权应当遵循最小权限授予法,即仅授予满足用户需要的最小权限。这是因为在数据库中,如果进行了不当授权,则可能为数据库带来安全风险。
在数据库中,EXECUTE/CREATE ANY PROCEDURE都是非常重要的权限,如果被授予普通用户,则可能为数据库带来安全风险。
以下是在Oracle Database 8.1.7中的测试,首先创建了两个测试用户:
Connected to: Oracle8i Enterprise Edition Release 8.1.7.4.0 - 64bit Production With the Partitioning option JServer Release 8.1.7.4.0 - 64bit Production SQL> create user hacker identified by hacker default tablespace users temporary 2 tablespace temp; User created. SQL> grant create session to hacker; Grant succeeded. SQL> grant execute any procedure to hacker; Grant succeeded. SQL> create user loser identified by loser default tablespace users temporary 2 tablespace temp; User created. SQL> grant connect to loser; Grant succeeded.
当使用测试用户连接后,EXECUTE ANY PROCEDURE的权限使用户具有了访问和执行dbms_sys_sql包的权限,这个Package可以被用户利用来获得更高的权限:
SQL> connect hacker/hacker Connected. SQL> desc sys.dbms_sys_sql PROCEDURE BIND_ARRAY Argument Name Type In/Out Default? ------------------------------ ----------------------- ------ -------- C NUMBER(38) IN NAME VARCHAR2 IN N_TAB TABLE OF NUMBER IN PROCEDURE BIND_ARRAY Argument Name Type In/Out Default? ------------------------------ ----------------------- ------ -------- C NUMBER(38) IN NAME VARCHAR2 IN C_TAB TABLE OF VARCHAR2(2000) IN .... PROCEDURE VARIABLE_VALUE_ROWID Argument Name Type In/Out Default? ------------------------------ ----------------------- ------ -------- C NUMBER(38) IN NAME VARCHAR2 IN VALUE ROWID OUT
通过如下一段代码,Hacker用户就可以很多DDL语句,诸如修改其他用户的口令,对自我进行授权等:
SQL> connect hacker/hacker Connected. SQL> DECLARE 2 UID NUMBER; 3 sqltext VARCHAR2 (100) := 'alter user loser identified by test'; 4 c INTEGER; 5 BEGIN 6 c := SYS.DBMS_SYS_SQL.open_cursor (); 7 SYS.DBMS_SYS_SQL.parse_as_user (c, sqltext, DBMS_SQL.native, 0); 8 SYS.DBMS_SYS_SQL.close_cursor (c); 9 END; 10 / PL/SQL procedure successfully completed.
通过DBMS_SYS_SQL.parse_as_user,hacker可以在数据库内任意为非作歹了。用户loser的口令已被更改:
SQL> connect loser/loser ERROR: ORA-01017: invalid username/password; logon denied Warning: You are no longer connected to ORACLE. SQL> connect loser/test Connected.
在Oracle 9i中,如果用户具有EXECUTE/CREATE ANY PROCEDURE的权限,则同样用户可以做出很多跨越常规的授权:
SQL> select * from v$version where rownum <2; BANNER ---------------------------------------------------------------- Oracle9i Enterprise Edition Release 9.2.0.4.0 - 64bit Production SQL> create user eygle identified by eygle; User created. SQL> grant create session to eygle; Grant succeeded. SQL> grant execute any procedure to eygle; Grant succeeded.
现在CREATE ANY PROCEDURE的权限可以使用户创建任何过程:
SQL> eygle/eygle SQL> CREATE OR REPLACE PROCEDURE SYSTEM.DO(P_TEXT VARCHAR2) 2 IS 3 BEGIN 4 EXECUTE IMMEDIATE P_TEXT; 5 END; 6 / Procedure created.
现在EXECUTE ANY PROCEDURE的权限,可以使用户执行创建的过程,进行跨越职权的授权:
SQL> execute system.do('grant dba to eygle'); PL/SQL procedure successfully completed.
检查一下当前用户具有的权限:
SQL> SELECT * FROM SESSION_PRIVS; PRIVILEGE ---------------------------------------- CREATE SESSION UNLIMITED TABLESPACE CREATE ANY PROCEDURE EXECUTE ANY PROCEDURE SQL> SELECT * FROM SESSION_ROLES; ROLE ------------------------------ PLUSTRACE
重新连接会激活角色授权,可以注意到用户已经具备了DBA的职权:
SQL> connect eygle/eygle Connected. SQL> SELECT * FROM SESSION_ROLES; ROLE ------------------------------ PLUSTRACE DBA SELECT_CATALOG_ROLE HS_ADMIN_ROLE EXECUTE_CATALOG_ROLE DELETE_CATALOG_ROLE EXP_FULL_DATABASE IMP_FULL_DATABASE GATHER_SYSTEM_STATISTICS WM_ADMIN_ROLE JAVA_ADMIN JAVA_DEPLOY 12 rows selected. SQL> SELECT * FROM SESSION_PRIVS; PRIVILEGE ---------------------------------------- ALTER SYSTEM AUDIT SYSTEM CREATE SESSION ALTER SESSION RESTRICTED SESSION CREATE TABLESPACE ALTER TABLESPACE MANAGE TABLESPACE 。。。。。。。。
虽然在不同的版本中,Oracle一直在强化其安全管理,但是仍然需要我们小心谨慎的管理数据库权限,确保数据库安全。
数据字典的访问
很多时候由于应用的需要,数据库用户需要访问数据字典对象,而这些数据字典对象对于数据库的稳定运行非常重要,所以Oracle需要通过权限设置来限制这些对象的访问。
在现实的很多系统中,通常具有这一类访问需求的用户被授予了DBA的权限,这是非常危险的,给予适当的用户以适当的权限是权限管理的基本原则。
本节我们就数据字典的访问权限进行探讨。
访问数据字典需要对用户授予系统权限,而Oracle的大部分系统权限都具有很大的权力,因此在给非DBA用户授予系统权限时一定要非常谨慎。例如,具有UPDATE ANY TABLE系统权限的用户,就拥有更改SYS用户数据字典基表的权限(例如obj$,ts$等)。
在数据字典的访问权限上,Oracle权限管理的发展经历了几个过程,这几个发展过程积累了如下一些参数和权限:
缺省的数据字典基础表仅限具有SYSOPER或SYSDBA权限的用户,通过授予相应的权限,其他用户可以获得数据字典表的访问权限。为了控制普通用户对于字典表的访问,Oracle引入了O7_DICTIONARY_ACCESSIBILITY初始化参数。
在Oracle9i之前,这个参数缺省值为TRUE。在Oracle8中,通过这个参数的机制可以控制用户对于字典对象的访问。我们看一下Oracle8i环境中这一参数的作用:
也许你想不到,SQL*Plus在Oracle 8.1.5中,show parameter的功能是区分大小写的,有时候回顾一下Oracle的不同版本是很有意思的一件事(注意该参数的缺省值为TRUE):
SQL> select * from v$version where rownum <2; BANNER ---------------------------------------------------------------- Oracle8i Enterprise Edition Release 8.1.5.0.0 - Production SQL> show parameter o7 SQL> show parameter O NAME TYPE VALUE ------------------------------ ------- ---------------------------------------- O7_DICTIONARY_ACCESSIBILITY boolean TRUE
我们创建一个全新的用户,并且授予create session的权限:
SQL> select username from dba_users where username='EYGLE'; no rows selected SQL> create user eygle identified by eygle default tablespace users temporary tablespace temp; User created. SQL> grant create session to eygle; Grant succeeded.
使用新的用户登录之后,我们可以看到这个用户仅具备CREATE SESSION的权限,不能访问数据字典对象
SQL> connect eygle/eygle Connected. SQL> select * from session_privs; PRIVILEGE ---------------------------------------- CREATE SESSION SQL> desc sys.obj$ ERROR: ORA-04043: object sys.obj$ does not exist
我们再来看一下在O7_DICTIONARY_ACCESSIBILITY为True的设置下,SELECT ANY TABLE权限的作用:
SQL> connect / as sysdba Connected. SQL> grant select any table to eygle; Grant succeeded. SQL> connect eygle/eygle Connected. SQL> select * from session_privs; PRIVILEGE ---------------------------------------- CREATE SESSION SELECT ANY TABLE SQL> select count(*) from sys.obj$; COUNT(*) ---------- 16389 SQL> select count(*) from scott.emp; COUNT(*) ---------- 14
我们注意到此时用户具备了查询数据字典的权限,而当该参数设置为FALSE时,SELECT ANY TABLE的系统权限并不能使用户获得访问数据字典的权限,但是仍然可以访问其他用户的数据表:
SQL> show parameter O7 NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ O7_DICTIONARY_ACCESSIBILITY boolean FALSE SQL> select * from session_privs; PRIVILEGE ---------------------------------------- CREATE SESSION SELECT ANY TABLE SQL> select count(*) from sys.obj$; select count(*) from sys.obj$ * ERROR at line 1: ORA-00942: table or view does not exist SQL> desc sys.v$instance ERROR: ORA-04043: object sys.v$instance does not exist SQL> select count(*) from scott.emp; COUNT(*) ---------- 14
也就是说Oracle通过O7_DICTIONARY_ACCESSIBILITY将部分ANY权限对访问数据字典和普通用户对象的权限界定开来。
为了安全起见,从Oracle9i开始,O7_DICTIONARY_ACCESSIBILITY参数缺省值即为FALSE。也正因为如此,从Oracle9i开始,SYS用户必需以SYSDBA或SYSOPER身份登录数据库,否则将收到如下错误提示:
SQL> connect sys/oracle ERROR: ORA-28009: connection to sys should be as sysdba or sysoper Warning: You are no longer connected to ORACLE.
而如果将O7_DICTIONARY_ACCESSIBILITY参数设置为TRUE,SYS用户登录时就不会出现该错误提示:
SQL> connect sys/oracle Connected. SQL> show parameter o7 NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ O7_DICTIONARY_ACCESSIBILITY boolean TRUE
为了弥补O7_DICTIONARY_ACCESSIBILITY参数设置为FALSE带来的影响,在Oracle9i中,Oracle引入了SELECT ANY DICTIONARY的系统权限,这个权限可以使用户获得数据字典的访问权限:
SQL> connect eygle/eygle Connected. SQL> select * from session_privs; PRIVILEGE ---------------------------------------- CREATE SESSION SELECT ANY DICTIONARY SQL> select count(*) from sys.obj$; COUNT(*) ---------- 6431 SQL> select count(*) from v$instance; COUNT(*) ---------- 1 SQL> select count(*) from scott.emp; select count(*) from scott.emp * ERROR at line 1: ORA-00942: table or view does not exist
通过测试我们注意到,如果仅有SELECT ANY DICTIONARY可以使用户具备访问数据字典的权限,但是不能使用户获得访问其他用户表的权限。
也就是说:
O7_DICTIONARY_ACCESSIBILITY=TRUE + SELECT ANY TABLE
=
O7_DICTIONARY_ACCESSIBILITY=FALSE + SELECT ANY TABLE + SELECT ANY DICTIONARY
SELECT_CATALOG_ROLE角色是Oracle8中引入的,用于限制用户对于SYS拥有的视图的访问:
SQL> revoke select any dictionary from eygle; Revoke succeeded. SQL> grant select_catalog_role to eygle; Grant succeeded. SQL> connect eygle/eygle Connected. SQL> select * from session_privs; PRIVILEGE ---------------------------------------- CREATE SESSION SQL> select * from session_roles; ROLE ------------------------------ SELECT_CATALOG_ROLE SQL> select count(*) from dba_tables; COUNT(*) ---------- 520 SQL> select count(*) from sys.tab$; select count(*) from sys.tab$ * ERROR at line 1: ORA-00942: table or view does not exist