109.3 数据库授权方法
一旦使用数据库验证了用户,下一步就是确定用户可以有权访问或使用的对象类型、权限和资源。本节中将介绍配置文件控制管理密码的方式,还将介绍配置文件在各种类型的系统资源上添加限制的方式。
此外,本节将讨论Oracle数据库中两种类型的权限:系统权限和对象权限。这两种权限都可以直接赋予用户,或者通过角色间接赋予用户,这是另一种在将权限赋予用户时可以简化DBA工作的机制。
本节的末尾将概述Oracle的虚拟专用数据库(VPD)特性,以及如何使用此特性将其用于对如下情况提供更精确的地控制:用户根据赋予自己的一组DBA定义的证书来可以查看表的某些哪些部分。为了帮助使这一概念更加清晰,我们将完整地实现一个VPD。
109.3.1 配置文件的管理
看起来似乎总是不会有足够的CPU功率或磁盘空间或I/O带宽来运行用户的查询。因为所有这些资源本来就是有限的,Oracle提供了一种机制来控制用户可以使用多少资源,Oracle配置文件就是提供这种机制的指定资源限制集。
此外,配置文件可用作授权机制来控制如何创建、重用和验证用户密码。例如,可能希望实施最小的密码长度,同时需要密码中至少出现一个大写字母和一个小写字母。本节中将讨论配置文件如何管理密码和资源。
1. CREATE PROFILE命令
create profile命令有双重用途。可以创建配置文件,将用户的连接时间限制为120分钟。
create profile lim_connect limit
connect_time 120;
类似地,可以限制在锁定账户之前注册登录可以连续失败的次数:
create profile lim_fail_login limit
failed_login_attempts 8;
或者,可以将这两种类型的限制合并结合在一个配置文件中:
create profile lim_connectime_faillog limit
connect_time 120
failed_login_attempts 8;
Oracle如何响应超出的一种资源限制取决于限制的类型。当到达一个连接时间限制或空闲时间限制(如CPU_PER_SESSION)时,回滚进行中的事务,并且取消会话连接。对于大多数其他的资源限制(如PRIVATE_SGA),回顾回滚当前的事务,将一个错误返回给用户,并且用户可以选择提交或回滚事务。如果操作超出某个调用的限制(如LOGICAL_READS_PER_ CALL),则中断该操作,回滚当前的语句,并且将一个错误返回给用户。事务的剩余部分保持不变,然后,用户可以回滚、提交或尝试在不超出语句限制的情况下完成事务。
Oracle提供了DEFAULT配置文件,如果没有指定其他的配置文件,则将该配置文件应用于任何新的用户。下面针对数据字典视图DBA_PROFILES的查询显示了DEFAULT配置文件的 限制:
SQL> select * from dba_profiles
2 where profile = 'DEFAULT';
PROFILE RESOURCE_NAME RESOURCE LIMIT
------------- ----------------------- -------- -------------
DEFAULT COMPOSITE_LIMIT KERNEL UNLIMITED
DEFAULT SESSIONS_PER_USER KERNEL UNLIMITED
DEFAULT CPU_PER_SESSION KERNEL UNLIMITED
DEFAULT CPU_PER_CALL KERNEL UNLIMITED
DEFAULT LOGICAL_READS_PER_SESSION KERNEL UNLIMITED
DEFAULT LOGICAL_READS_PER_CALL KERNEL UNLIMITED
DEFAULT IDLE_TIME KERNEL UNLIMITED
DEFAULT CONNECT_TIME KERNEL UNLIMITED
DEFAULT PRIVATE_SGA KERNEL UNLIMITED
DEFAULT FAILED_LOGIN_ATTEMPTS PASSWORD 10
DEFAULT PASSWORD_LIFE_TIME PASSWORD 180
DEFAULT PASSWORD_REUSE_TIME PASSWORD UNLIMITED
DEFAULT PASSWORD_REUSE_MAX PASSWORD UNLIMITED
DEFAULT PASSWORD_VERIFY_FUNCTION PASSWORD NULL
DEFAULT PASSWORD_LOCK_TIME PASSWORD 1
DEFAULT PASSWORD_GRACE_TIME PASSWORD 7
16 rows selected.
SQL> select * from dba_profiles
2 where profile = 'DEFAULT';
PROFILE RESOURCE_NAME RESOURCE LIMIT
--------------- -------------------------------- -------- ------------
DEFAULT COMPOSITE_LIMIT KERNEL UNLIMITED
DEFAULT SESSIONS_PER_USER KERNEL UNLIMITED
DEFAULT CPU_PER_SESSION KERNEL UNLIMITED
DEFAULT CPU_PER_CALL KERNEL UNLIMITED
DEFAULT LOGICAL_READS_PER_SESSION KERNEL UNLIMITED
DEFAULT LOGICAL_READS_PER_CALL KERNEL UNLIMITED
DEFAULT IDLE_TIME KERNEL UNLIMITED
DEFAULT CONNECT_TIME KERNEL UNLIMITED
DEFAULT PRIVATE_SGA KERNEL UNLIMITED
DEFAULT FAILED_LOGIN_ATTEMPTS PASSWORD 10
DEFAULT PASSWORD_LIFE_TIME PASSWORD UNLIMITED
DEFAULT PASSWORD_REUSE_TIME PASSWORD UNLIMITED
DEFAULT PASSWORD_REUSE_MAX PASSWORD UNLIMITED
DEFAULT PASSWORD_VERIFY_FUNCTION PASSWORD NULL
DEFAULT PASSWORD_LOCK_TIME PASSWORD UNLIMITED
DEFAULT PASSWORD_GRACE_TIME PASSWORD UNLIMITED
DEFAULT配置文件中唯一真正的约束将锁定账户前连续不成功的注册登录尝试数量(FAILED_LOGIN_ATTEMPTS)限制为10,将必须改变密码前此密码可以使用的天数 (PASSWORD_LIFE_TIME)设置为180。此外,没有启用任何密码验证功能。
2. 配置文件和密码控制
表109-4中是密码相关的配置文件参数。按照天数指定所有时间单位(例如,为了以分钟为单位指定这些参数,可以将其除以1440):
SQL> create profile lim_lock limit password_lock_time 5/1440;
Profile created.
SQL> create profile lim_lock limit password_lock_time 5/1440;
在该示例中,在指定数量的注册登录失败指定的次数后,账户将只锁定5分钟。
表109-4 密码相关的配置文件参数
密 码 参 数 | 说 明 |
FAILED_LOGIN_ATTEMPTS | 锁定账户前失败的注册登录尝试的数量次数 |
PASSWORD_LIFE_TIME | 在必须改变密码前可以使用该密码的天数。如果没有在PASSWORD_GRACE_TIME中进行改动,则必须在允许注册登录前改变该密码 |
PASSWORD_REUSE_TIME | 用户在重新使用密码前必须等待的天数;该参数和PASSWORD_REUSE_MAX结合起来使用 |
PASSWORD_REUSE_MAX | 在可以重用密码前必须进行的密码改动次数;该参数和PASSWORD_REUSE_TIME结合起来使用 |
PASSWORD_LOCK_TIME | 在FAILED_LOGIN_ATTEMPTS尝试后锁定账户的天数。在这个时间周期后,账户自动解除锁定 |
PASSWORD_GRACE_TIME | 在多少天之后必须改变到期密码必须改变后的天数。如果没有在这个时间周期内进行改动,则账户到期,并且必须在用户可以成功注册登录之前改变该密码 |
PASSWORD_VERIFY_FUNCTION | PL/SQL脚本,用于提供高级密码验证例程。如果指定为NULL(默认值),则不执行任何密码验证 |
参数值unlimited表示没有限制可以使用的给定资源数量。dDefault表示该参数从DEFAULT配置文件中获得它的值。
参数 password_reuse_time和password_reuse_max必须同时使用。设置其中一个参数,而不设置另一个参数,则不会有任何作 用。在下面的示例中,创建一个配置文件,将password_reuse_time设置为20天,而将password_reuse_max设置为5:
create profile lim_reuse_pass limit
password_reuse_time 20
password_reuse_max 5;
对于具有该配置文件的用户,如果他们的密码至少已经改变了5次,则这些密码可以在20天后重新使用。如果为其中一个参数指定一个值,而为另一个参数指定UNLIMITED,则用户可以永远不重用密码。
和大多数其他的操作一样,可以使用Oracle Enterprise Manager可以很容易地管理配置文件。图109-4显示了一个示例:将DEFAULT配置文件改为在15分钟不活动后取消用户连接。
图109-4 使用Oracle Enterprise Manager改变密码限制
如果希望对如何创建和重用密码提供更紧密严格的 控制,例如在每个密码中混合使用大写字母和小写字母,则需要在每个应用程序配置文件中启用PASSWORD_VERIFY_FUNCTION限制。 Oracle提供了一个模板来实施组织的密码策略。该模板位于$ORACLE_HOME/rdbms/admin/ utlpwdmg.sql。附录A中给出了该脚本的一些关键部分如下:
CREATE OR REPLACE FUNCTION verify_function_11G
(username varchar2,
password varchar2,
old_password varchar2)
RETURN boolean IS
n boolean;
m integer;
differ integer;
isdigit boolean;
ischar boolean;
ispunct boolean;
db_name varchar2(40);
digitarray varchar2(20);
punctarray varchar2(25);
chararray varchar2(52);
i_char varchar2(10);
simple_password varchar2(10);
reverse_user varchar2(32);
BEGIN
digitarray:= '0123456789';
chararray:= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
. . .
-- Check if the password is same as the username reversed
FOR i in REVERSE 1..length(username) LOOP
reverse_user := reverse_user || substr(username, i, 1);
END LOOP;
IF NLS_LOWER(password) = NLS_LOWER(reverse_user) THEN
raise_application_error(-20003, 'Password same as username reversed');
END IF;
. . .
-- Everything is fine; return TRUE ;
RETURN(TRUE);
END;
/
-- This script. alters the default parameters for Password Management
-- This means that all the users on the system have Password Management
-- enabled and set to the following values unless another profile is
-- created with parameter values set to different value or UNLIMITED
-- is created and assigned to the user.
ALTER PROFILE DEFAULT LIMIT
PASSWORD_LIFE_TIME 180
PASSWORD_GRACE_TIME 7
PASSWORD_REUSE_TIME UNLIMITED
PASSWORD_REUSE_MAX UNLIMITED
FAILED_LOGIN_ATTEMPTS 10
PASSWORD_LOCK_TIME 1 PASSWORD_VERIFY_FUNCTION verify_function_11G;
一个缩写版本。
该脚本为密码复杂性提供了如下功能函数:
● 确保密码与用户名不同。
● 确保密码至少具有4个字符长。
● 进行检查,确保密码不是简单的、显而易见的单词,例如ORACLE或DATABASE。
● 需要密码包含一个字母、一个数字以及一个标点符号。
● 确保密码与前面的密码至少有3个字符不同。
为了使用这一策略,首先应对该脚本进行自定义改动。例如,可能希望具有几个一些不同的验证函数,每一个函数种功能针对一个国家或一个业务部门企业单位,用于将数据库密码复杂性需求匹配特定国家或企业单位业务部门中使用的操作系统的需求。例如,可以将这种函数重命名为VERIFY_FUNCTION_ US_MIDWEST。此外,可能希望将简单单词的列表改为包括公司的中部门名称或建筑大楼的名称。
一旦成功编译该函数,可以通过alter profile命令,改变已有的配置文件以使用该函数,或者可以创建使用该函数的新的配置文件。在下面的示例中,改变DEFAULT配置文件以使用函数VERIFY_FUNCTION_US_MIDWEST:
SQL> alter profile default limit
2 password_verify_function verify_function_us_midwest;
Profile altered.
SQL> alter profile default limit
password_verify_function verify_function_us_midwest;
对于所有使用DEFAULT配置文件的已有用户,或 者是使用DEFAULT配置文件的新用户,通过VERIFY_FUNCTION_US_MIDWEST函数检查他们的密码。如果该函数返回不同于TRUE 的值,则不允许该密码,用户必须指定不同的密码。如果用户的当前密码不符合该函数中的规则,该密码仍然有效,直到改变密码,此时该函数必须验证新的密码。
3. 配置文件和资源控制
表109-5中列出了使用CREATE PROFILE profilename LIMIT后可以出现的资源控制配置文件选项列表。每个参数都可以是整数、UNLIMITED或DEFAULT。
表109-5 与资源相关的配置文件参数
资 源 参 数 | 说 明 |
SESSIONS_PER_USER | 用户可以同时具有的最大会话数量 |
CPU_PER_SESSION | 每个会话允许的最大CPU时间,以1%秒为单位 |
CPU_PER_CALL | 语句解析、执行或读取操作的最大CPU时间,以1%秒为单位 |
CONNECT_TIME | 最大总计消耗时间,以分钟为单位 |
IDLE_TIME | 当查询或其他操作停止执行时,会话中的最大连续不活动时间,以分钟为单位,同时查询或其他操作没有在进行中 |
LOGICAL_READS_PER_SESSION | 每个会话从内存或磁盘中读取的数据块总量 |
LOGICAL_READS_PER_CALL | 语句解析、执行或读取操作的最大数据块读取量 |
COMPOSITE_LIMIT | 以服务单位划分的总计资源成本,作为CPU_PER_SESSION、CONNECT_TIME、LOGICAL_READS_PER_SESSION和PRIVATE_SGA的组合加权和 |
PRIVATE_SGA | 会话可以在共享池中分配的最大内存量,以字节、千字节或兆字节为单位 |
和密码相关的参数一样,UNLIMITED表示没有限制可以使用的资源数量。DEFAULT表示该参数从DEFAULT配置文件中获得它的值。
COMPOSITE_LIMIT参数允许在使用的资源类型剧烈变化时控制一组资源限制。它允许用户在一个会话期间使用大量CPU时间和较少的磁盘I/O,而在另一个会话期间采用相反的情况,但不需要通过策略来保持取消连接。
默认情况下,所有的资源成本为0:
SQL> select * from resource_cost;
RESOURCE_NAME UNIT_COST
-------------------------------- ----------
CPU_PER_SESSION 0
LOGICAL_READS_PER_SESSION 0
CONNECT_TIME 0
PRIVATE_SGA 0
4 rows selected.
SQL> select * from resource_cost;
RESOURCE_NAME UNIT_COST
-------------------------------- ----------
CPU_PER_SESSION 0
LOGICAL_READS_PER_SESSION 0
CONNECT_TIME 0
PRIVATE_SGA 0
为了调整资源成本的权值,可以使用ALTER RESOURCE COST命令。在下面的示例中,改变加权,从而CPU_PER_SESSION更关注CPU利用率而不是连接时间,其比例系数为25:1。换句话说,用户 将更可能由于CPU利用率(而不是连接时间)而取消连接:
SQL> alter resource cost
2 cpu_per_session 50
3 connect_time 2;
Resource cost altered.
SQL> select * from resource_cost;
RESOURCE_NAME UNIT_COST
-------------------------------- ----------
CPU_PER_SESSION 50
LOGICAL_READS_PER_SESSION 0
CONNECT_TIME 2
PRIVATE_SGA 0
4 rows selected.
SQL> alter resource cost
cpu_per_session 50
connect_time 2;
Resource cost altered.
SQL> select * from resource_cost;
RESOURCE_NAME UNIT_COST
-------------------------------- ----------
CPU_PER_SESSION 50
LOGICAL_READS_PER_SESSION 0
CONNECT_TIME 2
PRIVATE_SGA 0
下一步是创建新的配置文件或修改已有的配置文件,从而可以使用组合的限制:
SQL> create profile lim_comp_cpu_conn limit
2 composite_limit 250;
Profile created.
SQL> create profile lim_comp_cpu_conn limit
composite_limit 250;
作为因此,赋予配置文件LIM_COMP_CPU_CONN的用户将使用下面的公式计算成本,从而限制他们的会话资源:
composite_cost = (50 * CPU_PER_SESSION) + (2 * CONNECT_TIME);
在表109-6中,提供了一些资源利用的一些示例,用于查看是否超出了组合限制250。
表109-6 资源利用情况
CPU(秒) | 连接(秒) | 组合的成本 | 是 否 超 出 |
0.05 | 100 | (50*5) + (2*100) = 450 | 是 |
0.02 | 30 | (50*2) + (2*30) = 160 | 否 |
0.01 | 150 | (50*1) + (2*150) = 350 | 是 |
0.02 | 5 | (50*2) + (2*5) = 110 | 否 |
在这个特定 的示例中没有使用参数PRIVATE_SGA和LOGICAL_READS_PER_SESSION,除非在配置文件定义中的其他位置指定它们,否则它们 默认为在DEFAULT配置文件中的值。使用组合限制的目的在于用户可以运行更多类型的查询或DML。在某些天中,他们可能运行许多查询,这些查询执行大 量计算,但是没有访问过多的表行。在其他一些天中,他们可能执行许多完整的表扫描,但是没有保持长时间的连接。在这些情况中,不希望通过单一的一个参数来限制用户,而是通过总计的资源利用率来限制用户,该资源利用率按照服务器上每个资源的可用性的加权来获得该利用率。
109.3.2 系统权限
系统权限是在数据库中任何对象上执行操作的权利,以及其他一些权限,这些权限完全不涉及对象,而是涉及运行批处理工作作业、改变系统参数、创建角色、甚至是连接到数据库自身等方面。Oracle 10g11g的版本1中有173206个系统权限。可以在数据字典表SYSTEM_PRIVILEGE_MAP中看到所有这些权限:
SQL> select * from system_privilege_map;
PRIVILEGE NAME PROPERTY
---------- ------------------------------------- ----------
-3 ALTER SYSTEM 0
-4 AUDIT SYSTEM 0
-5 CREATE SESSION 0
-6 ALTER SESSION 0
-7 RESTRICTED SESSION 0
-10 CREATE TABLESPACE 0
-11 ALTER TABLESPACE 0
-12 MANAGE TABLESPACE 0
-13 DROP TABLESPACE 0
-15 UNLIMITED TABLESPACE 0
-20 CREATE USER 0
-21 BECOME USER 0
-22 ALTER USER 0
-23 DROP USER 0
. . .
-318 INSERT ANY MEASURE FOLDER 0
-319 CREATE CUBE BUILD PROCESS 0
-320 CREATE ANY CUBE BUILD PROCESS 0
-321 DROP ANY CUBE BUILD PROCESS 0
-322 UPDATE ANY CUBE BUILD PROCESS 0
-326 UPDATE ANY CUBE DIMENSION 0
-327 ADMINISTER SQL MANAGEMENT OBJECT 0
-350 FLASHBACK ARCHIVE ADMINISTER 0
206 rows selected.
SQL> select * from system_privilege_map;
PRIVILEGE NAME PROPERTY
---------- --------------------------------------- ----------
-3 ALTER SYSTEM 0
-4 AUDIT SYSTEM 0
-5 CREATE SESSION 0
-6 ALTER SESSION 0
-7 RESTRICTED SESSION 0
-10 CREATE TABLESPACE 0
-11 ALTER TABLESPACE 0
-12 MANAGE TABLESPACE 0
-13 DROP TABLESPACE 0
-15 UNLIMITED TABLESPACE 0
-20 CREATE USER 0
-21 BECOME USER 0
-22 ALTER USER 0
-23 DROP USER 0
-271 ALTER ANY SQL PROFILE 0
-272 ADMINISTER SQL TUNING SET 0
-273 ADMINISTER ANY SQL TUNING SET 0
-274 CREATE ANY SQL PROFILE 0
-275 EXEMPT IDENTITY POLICY 0
表109-7列出了一些更为常见的系统权限,并且简要描述了这些权限。
表109-7 常见的系统权限
系 统 权 限 | 功 能 |
ALTER DATABASE | 对数据库进行改动,例如将数据库状态从MOUNT改为OPEN,或者是恢复数据库 |
ALTER SYSTEM | 发布ALTER SYSTEM语句:切换到下一个重做日志组,改变或者在SPFILE中的改变系统初始参数 |
AUDIT SYSTEM | 发布AUDIT语句 |
CREATE DATABASE LINK | 创建到远程数据库的数据库链接 |
CREATE ANY INDEX | 在任意模式中创建索引;针对用户的模式,连同随同CREATE TABLE一起授权CREATE INDEX |
(续表)
系 统 权 限 | 功 能 |
CREATE PROFILE | 创建资源/密码配置文件 |
CREATE PROCEDURE | 在自己的模式中创建函数、过程或程序包 |
CREATE ANY PROCEDURE | 在任意的模式中创建函数、过程或程序包 |
CREATE SESSION | 连接到数据库 |
CREATE SYNONYM | 在自己的模式中创建私有同义词 |
CREATE ANY SYNONYM | 在任意模式中创建私有同义词 |
CREATE PUBLIC SYNONYM | 创建公有同义词 |
DROP ANY SYNONYM | 在任意模式中删除私有同义词 |
DROP PUBLIC SYNONYM | 删除公有同义词 |
CREATE TABLE | 在自己的模式中创建表 |
CREATE ANY TABLE | 在任意模式中创建表 |
CREATE TABLESPACE | 在数据库中创建新的表空间 |
CREATE USER | 创建用户账户/模式 |
ALTER USER | 改动用户账户/模式 |
CREATE VIEW | 在自己的模式中创建视图 |
SYSDBA | 如果启用了外部密码文件,则在外部密码文件中创建一个条目,如果启用外部密码文件的话;同时,执行启动/关闭数据库,改变数据库,创建数据库,恢复数据库,创建SPFILE,以及当数据库处于RESTRICTED SESSION模式时连接数据库 |
SYSOPER | 如果启用了外部密码文件,则在外部密码文件中创建一个条目,如果启用外部密码文件的话;同时,执行启动/关闭数据库,改变数据库,创建数据库,恢复数据库,创建SPFILE,以及当数据库处于RESTRICTED SESSION模式时连接数据库 |
1. 授予系统权限
使用grant命令将权限授予用户、角色或PUBLIC,使用revoke命令取消权限。PUBLIC是一个特殊的组,包含所有的数据库用户,通过它可以方便快捷地将权限授予数据库中的每 个人。
为了授予用户SCOTT创建存储过程和同义词的能力,可以使用类似于如下的命令:
SQL> grant create procedure, create synonym to scott;
Grant succeeded.
取消权限也非常容易:
SQL> revoke create synonym from scott;
Revoke succeeded.
如果希望允许被授权者有权将相同的权限授予其他某个人,可以在授予权限时包括with admin option选项。在前面的示例中,希望用户SCOTT能够将CREATE PROCEDURE权限授予其他用户。为了实现这一点,需要重新授予CREATE PROCEDURE权限:
SQL> grant create procedure to scott with admin option;
Grant succeeded.
现在用户SCOTTScott可以发布grant create procedure命令。注意,如果取消SCOTT将Scott授予该权限授予其他人的许可,他已经授予权限的用户将保留该权限。
2. 系统权限数据字典视图
表109-8包含了与系统权限相关的数据字典视图。
表109-8 系统权限数据字典视图
数据字典视图 | 说 明 |
DBA_SYS_PRIVS | 赋予角色和用户的系统权限 |
SESSION_PRIVS | 对该会话的这个用户有效的所有系统权限,直接授权或通过角色 |
ROLE_SYS_PRIVS | 通过角色授权给用户的当前会话权限 |
109.3.3 对象权限
与系统权限相比,对象权限是在特定对象(如表或序列)上执行特定类型操作的权利,该对象不在用户自己的模式中。和系统权限一样,使用grant和revoke命令来授予或和取消对象上的 权限。
和系统权限一样,可以授予对象权限给PUBLIC或特定用户,并且具有对象权限的用户可以将其传递给其他用户,其方法是使用with grant option子句授予对象权限。
警告:
当所有当前的和未来的数据库用户确切地需要权限时,只将对象权限或系统权限授予PUBLIC。
一些模式对象,例如集群和索引,依赖于系统权限来控制访问。在这些情况中,如果用户拥有这些对象或具有ALTER ANY CLUSTER或ALTER ANY INDEX系统权限,则可以改变这些对象。
在自己的模式中拥有对象的用户自动具有这些对象上的所有对象权限,并且可以将这些对象上的任何对象权限授予任意用户或另一个角色,使用或不使用grant option子句。
表109-9中是可用于不同类型对象的对象权限,一些权限只适用于某些类型的对象。例如,INSERT权限只对表、视图和物化视图有意义。另一方面,EXECUTE权限适用于函数、过程和程序包,但不适用于表。
表109-9 对象权限
对 象 权 限 | 功 能 |
ALTER | 可以改变表或序列的定义 |
DELETE | 可以从表、视图或物化视图中删除行 |
EXECUTE | 可以执行函数或过程,使用或不使用程序包 |
DEBUG | 允许查看在表上定义的触发器中的PL/SQL代码,或者查看引用表的SQL语句。对于对象类型,该权限允许访问在对象类型上定义的所有共有和私有变量、方法和类型 |
FLASHBACK | 允许使用保留的撤销信息在表、视图和物化视图中进行闪回查询 |
INDEX | 可以在表上创建索引 |
INSERT | 可以在向表、视图或物化视图中插入行 |
ON COMMIT REFRESH | 可以根据表创建提交后刷新的物化视图 |
QUERY REWRITE | 可以根据表创建用于查询重写的物化视图 |
READ | 可以使用Oracle DIRECTORY定义读取操作系统目录的内容 |
REFERENCES | 可以创建引用另一个表的主键或唯一键的外键约束 |
SELECT | 可以从表、视图或物化视图中读取行,此外还可以从序列中读取当前值或下面的值 |
UNDER | 可以根据已有的视图创建视图 |
UPDATE | 可以更新表、视图或物化视图中的行 |
WRITE | 可以使用Oracle DIRECTORY定义将信息写入到操作系统目录 |
值得注意的是,不可以将DELETE、UPDATE和INSERT权限授予物化视图,除非这些视图是可更新的。一些对象权限和系统权限重复;例如,如果没有表上的FLASHBACK对象权限,但只要有FLASHBACK ANY TABLE系统权限,就仍然可以执行闪回查询。
在下面的示例中,DBA授权SCOTT对表HR.EMPLOYEES的完全访问,但只允许SCOTT将SELECT对象权限授权传递给其他用户:
SQL> grant insert, update, delete on hr.employees to scott;
Grant succeeded.
SQL> grant select on hr.employees to scott with grant option;
Grant succeeded.
SQL> grant insert, update, delete on hr.employees to scott;
Grant succeeded.
SQL> grant select on hr.employees to scott with grant option;
注意,如果取消了SCOTT在表HR.EMPLOYEES上的SELECT权限,则也取消他授予该权限的用户的SELECT权限。
1. 表权限
可以在表上授予的权限类型主要分为两类:DML操作和DDL操作。DML操作包括delete、insert、select和update,而DDL操作包括添加、删除和改变表中的列,以及在表上创建索引。
授权表上的DML操作时,可以将这些操作限制为只针对某些列。例如,可能希望允许SCOTT查看和更新HR.EMPLOYEES中所有的行和列,除了SALARY列。为了做到这一点,首先需要取消表上已有的SELECT权限:
SQL> revoke update on hr.employees from scott;
Revoke succeeded.
接下来,让SCOTT更新除了SALARY列之外的所有列:
SQL> grant update (employee_id, first_name, last_name, email,
2 phone_number, hire_date, job_id, commission_pct,
3 manager_id, department_id)
4 on hr.employees to scott;
Grant succeeded.
SQL> grant update (employee_id, first_name, last_name, email,
phone_number, hire_date, job_id, commission_pct,
manager_id, department_id)
on hr.employees to scott;
SCOTT将能够更新HR.EMPLOYEES表中除了SALARY列之外的所有列:
SQL> update hr.employees set first_name = 'Stephen' where employee_id = 100;
1 row updated.
SQL> update hr.employees set salary = 50000 where employee_id = 203;
update hr.employees set salary = 50000 where employee_id = 203
*
ERROR at line 1:
ORA-01031: insufficient privileges
SQL> update hr.employees set first_name = ‘Stephen’ where employee_id = 100;
1 row updated.
SQL> update hr.employees set salary = 50000 where employee_id = 203;
update hr.employees set salary = 50000 where employee_id = 203
*
ERROR at line 1:
也可以使用基于Web的OEM工具也很容易地执行该操作,如图109-5所示。
图109-5 在Oracle Enterprise Manager中授予列权限
2. 视图权限
视图上的权限类似于在表上授予的权限。假设视图是可更新的,则可以选择、更新、删除或插入视图中的行,假设视图是可更新的。为了创建视图,首先需要CREATE VIEW系统权限(用于在自己的模式中创建视图)或CREATE ANY VIEW系统权限(用于在任意模式中创建视图)。即使是创建视图,也必须至少具有视图的底层表上的SELECT对象权限,以及INSERT、UPDATE和DELETE等对象权限(,如果希望在视图上执行这些操作,并且视图是可更新的)。作为选择,如果底层的对象不在自己的模式中,则可以有SELECT ANY TABLE、INSERT ANY TABLE、UPDATE ANY TABLE或DELETE ANY TABLE权限。
为了允许其他人使用您的视图,必须使用GRANT OPTION具有视图的基表上的许可,或者必须使用ADMIN OPTION具有系统权限。例如,如果创建针对HR.EMPLOYEES表的视图,则必须通过WITH GRANT OPTION子句授予HR.EMPLOYEES表上的SELECT系统对象权限,或者通过WITH ADMIN OPTION子句具有SELECT ANY TABLE系统权限。
3. 过程权限
对于过程、函数以及包含过程和函数的程序包,EXECUTE权限是唯一可以应用的对象权限。从Oracle 8i开始,可以从定义者、过程或函数的创建者、调用者、运行函数或过程的用户等的角度来运行过程和函数。
使用定义者的权利运行过程时,如同定义者自身运行该过程一样,定义者所有的权限都对过程中引用的对象有效。这是在私有数据库对象上实施约束的好方法:授予其他用户在过程上的EXECUTE许可,而没有授予引用对象上的任何许可。结果,定义者可以控制其他用户如何访问对象。
相反,使用调用者权利的过程需要调用者具有针对该过程中引用的任何对象的直接权利,例如SELECT和UPDATE。该过程可能引用了无限制限定的表ORDERS,并且如果数据库的所有用户都具有ORDERS表,则具有自己有的ORDERS表的任何用户都可以使用相同的过程。使用调用者权利的过程的另一个优点是在过程它们中启用该角色。本章后面将深入讨论角色。
默认情况下,使用定义者的权利创建过程。为了指定过程使用调用者的权利,必须在过程定义中包括关键字authid current_user,如同下面的示例所示:
create or replace procedure process_orders (order_batch_date date)
authid current_user as
begin
-- process user's ORDERS table here using invoker's rights,
-- all roles are in effect
end;
create or replace procedure process_orders (order_batch_date date)
authid current_user as
begin
process user’s ORDERS table here using invoker’s rights,
all roles are in effect
为了创建过 程,用户必须具有CREATE PROCEDURE或CREATE ANY PROCEDURE系统权限。对于正确编译的过程,用户必须具有针对过程中引用的所有对象的直接权限,即使在运行时,在使用调用者权利的过程中启用了角色 以获得这些相同的权限。为了允许其他用户访问过程,可以授予过程或程序包上的EXECUTE权限。
4. 对象权限数据字典视图
大量数据字典视图包含了赋予用户的对象权限的相关信息。表109-10列出了包含对象权限信息的最重要的视图。
表109-10 对象权限数据字典视图
数据字典视图 | 说 明 |
DBA_TAB_PRIVS | 授予角色和用户的表权限。包括将权限授予角色或用户的用户,使用或不使用GRANT OPTION |
DBA_COL_PRIVS | 授予角色或用户的列权限。包含列名和列上的权限类型 |
SESSION_PRIVS | 对会话的该用户有效的所有系统权限,直接授予或通过角色 |
ROLE_TAB_PRIVS | 对于当前的会话,通过角色授予的表上的权限 |
109.3.4 创建、分配和维护角色
角色是一组指定的权限,这些权限是系统权限、对象权限或者两者的结合,用于帮助简化权限的管理。不同于单独将系统权限或对象权限授予每个用户,可以将一组系统权限或对象权限授予一个角色,然后将该角色授予用户。这将大量减少维护用户的权限所需的管理系统开销。图109-6显示了角色如何减少在将角色用于分组权限时需要执行的grant命令(最终是revoke命令)的数量。
图109-6 使用角色管理权限
如果需要改变由角色授权给一组人的权限,则只需要改变该角色的权限,并且具有该角色的用户有能力的功能将自动使用改动后的新权限。用户可以由用户有选择地启用角色,一些有些角色可以能在登录注册时就自动启用。此外,可以使用密码保护角色,添加对数据库中该功能的另一种验证级别。
在表109-11中是和数据库一起自动提供的最常见的角色,其中也简要描述了每个角色中的 权限。
表109-11 预定义的Oracle角色
角 色 名 | 权 限 |
CONNECT | Oracle Database 10g版本2之前的版本:ALTER SESSION、CREATE CLUSTER、CREATE DATABASE LINK、CREATE SEQUENCE、CREATE SESSION、CREATE SYNONYM、CREATE TABLE、CREATE VIEW。这些权限一般是提供给数据库中普通用户的权限,允许他们连接和创建表、索引以及视图。Oracle Database 10g版本2及之后的版本:只有CREATE SESSION |
(续表)
角 色 名 | 权 限 |
RESOURCE | CREATE CLUSTER、CREATE INDEXTYPE、CREATE OPERATOR、CREATE PROCEDURE、CREATE SEQUENCE、CREATE TABLE、CREATE TRIGGER、CREATE TYPE。这些权限一般用于可能正在编写码PL/SQL过程和函数的应用程序开发人员 |
DBA | 所有具有WITH ADMIN OPTION的系统权限。允许具有DBA角色的人将系统权限授予其他人 |
DELETE_CATALOG_ROLE | 没有任何系统权限,而只有SYS.AUD$和FGA_LOG$上的对象权限(DELETE)。换句话说,该角色允许用户从用于常规或细粒度审计的审计跟踪中删除审计记录 |
EXECUTE_CATALOG_ROLE | 各种系统程序包、过程和函数上的执行权限,例如DBMS_FGA和DBMS_RLS |
SELECT_CATALOG_ROLE | 1 638个数据字典表上的SELECT对象权限 |
EXP_FULL_DATABASE | EXECUTE_CATALOG_ROLE、SELECT_CATALOG_ROLE、以及诸如BACKUP ANY TABLE和RESUMABLE等系统权限。允许具有该角色的用户导出数据库中的所有对象 |
IMP_FULL_DATABASE | 类似于EXP_FULL_DATABASE,但是具有许多更多多很多的系统权限,例如CREATE ANY TABLE,用于允许导入前面导出的完整数据库 |
AQ_USER_ROLE | Advanced Queuing所需例程的执行访问,例如DBMS_AQ |
AQ_ADMINISTRATOR_ROLE_ROLE | Advanced Queuing查询的管理程序 |
SNMPAGENT | 由Enterprise Manager Intelligent Agent使用 |
RECOVERY_CATALOG_OWNER | 用户用于创建一个用户,该用户拥有用于RMAN备份和恢复的恢复目录 |
HS_ADMIN_ROLE | 提供对表HS_*和程序包DBMS_HRHS的访问,用于管理Oracle Heterogeneous Services |
SCHEDULER_ADMIN | 提供对程序包DBMS_SCHEDULER的访问,以及用于创建批处理工作作业的权限 |
提供角色CONNECT、RESOURCE和DBA主要是为了兼容以前的Oracle版本,而在将来的Oracle版本中可能不会有这些角色。数据库管理员应该使用授权给这些角色的权限作为起点来创建自定义的角色。
1. 创建或删除角色
为了创建角色,可以使用create role命令,并且必须具有CREATE ROLE系统权限。一般来说,该系统权限只授权给数据库管理员或应用程序管理员。下面是示例:
SQL> create role hr_admin not identified;
Role created.
SQL> create role hr_admin not identified;
默认情况下,启用或使用已分配的角色不需要任何密码或验证。因此,not identified子句是可 选项。
和创建用户一样,可以通过密码(使用identified by password的数据库授权)、通过操作系统(identified externally)、或者通过网络或目录服务(identified globally)授权使用角色。
除了这些熟悉的方法,还可以通过使用程序包授权角色:这称为使用“安全应用程序角色”。这种类型的角色使用程序包中的过程来启用角色。一般来说,只在某些条件下启用这种角色:用户正在通过Web接口或某个IP地址连接,或者是一天的某个时间。下面是使用过程启用的角色:
SQL> create role hr_clerk identified using hr.clerk_verif;
Role created.
SQL> create role hr_clerk identified using hr.clerk_verif;
创建角色时,过程HR.CLERK_VERIF不需 要存在。然而,当授予该角色的用户需要启用它时,它必须经过编译并且有效。一般来说,使用安全应用程序角色时,默认情况下不针对用户启用该角色。为了指定 在默认情况下启用除了安全应用程序角色之外的所有角色,可以使用如下的命令:
SQL> alter user kshelton default role all except hr_clerk;
User altered.
SQL> alter user wgietz default role all except hr_clerk;
通过这种方式,当HR应用程序启动时,它可以启用角色,其方法是执行set role hr_clerk命令,从而调用过程HR.CLERK_VERIF。用户不需要知道角色或启用角色的过程,因此,对象的访问和角色提供的权限都不可用于应用程序外部的用户。
删除角色和创建角色一样简单:
SQL> drop role keypunch_operator;
Role dropped.
SQL> drop role keypunch_operator;
下一次连接到数据库时,赋予该角色的任何用户将丢失赋予该角色的权限。如果他们当前已经注册登录,他们将保留这些权限,直到断开与取消数据库的连接。
2. 将权限授予角色
将权限赋予角色非常简单,可以使用grant命令将权限赋予角色,如同将权限赋予用户一样:
SQL> grant select on hr.employees to hr_clerk;
Grant succeeded.
SQL> grant create table to hr_clerk;
Grant succeeded.
SQL> grant select on hr.employees to hr_clerk;
Grant succeeded.
SQL> grant create trigger to hr_clerk;
在该示例中,将对象权限和系统权限赋予HR_CLERK角色。在图109-7中,可以使用基于Web的OEM来将更多的对象权限或系统权限添加给该角色。
图109-7 使用OEM将权限赋予角色
3. 分配或取消角色
一旦已经将所需的对象权限和系统权限赋予角色,就可以使用如下熟悉的语法将角色赋予 用户:
SQL> grant hr_clerk to smavris;
Grant succeeded.
SQL> grant hr_clerk to smavris;
SMAVRIS可以自动使用未来授予任何授权给HR_CLERK角色的其他任何权限在将来可以通过SMAVRIS使用,因为SMAVRIS已经被授予该角色。
角色可以授予其他角色,这就允许DBA设计多层次的 角色,从而使角色管理更为容易。例如,可能已经具有名为DEPT30、DEPT50和DEPT100的角色,每个角色具有一些对象权限,分别对应各个部门 的表。部门30中的雇员将分配DEPT30角色,依此类推。公司的董事长希望看到所有部门中的表,不必将单个的对象权限赋予角色ALL_DEPTS,而是 可以将单个的部门角色赋予ALL_DEPTS:
SQL> create role all_depts;
Role created.
SQL> grant dept30, dept50, dept100 to all_depts;
Grant succeeded.
SQL> grant all_depts to sking;
Grant succeeded.
SQL> create role all_depts;
Role created.
SQL> grant dept30, dept50, dept100 to all_depts;
Grant succeeded.
SQL> grant all_depts to sking;
角色ALL_DEPTS可能也包含单个对象权限和系统权限,这些权限不适用于单个部门,例如订单条目表或账户应收款项表上的对象权限。
从用户处取消角色非常类似于从用户处取消权限:
SQL> revoke all_depts from sking;
Revoke succeeded.
SQL> revoke all_depts from sking;
下次用户连接到数据库时,这些取消的权限将不再可用于这些用户。然而,值得注意的是,如果另一个角色包含与删除角色相同对象上的权限,或者直接授予对象上的权限,则用户将保留对象上的这些权限,直到显式地取消这些授权和所有其他授权。
4. 默认的角色
默认情况下,当用户连接到数据库时启用授予该用户的所有角色。如果角色将只用于应用程序的上下文中,则在用户注册登录时可以先禁用该角色,然后在应用程序中启用和禁用该角色。如果用户SCOTT具有CONNECT、RESOURCE、HR_CLERK和DEPT30角色,希望指定HR_CLERK和DEPT30默认情况下不启用,则可以使用类似于如下的代码:
SQL> alter user scott default role all
2> except hr_clerk, dept30;
User altered.
SQL> alter user scott default role all
2> except hr_clerk, dept30;
当SCOTT连接到数据库时,他自动具有通过除了除HR_CLERK和DEPT30之外的所有角色授予的所有权限。通过使用set role,SCOTT可以在他的会话中显式地启用一个角色:
SQL> set role dept30;
Role set.
当完成对部门30的表的访问时,可以在会话中禁用该角色:
SQL> set role all except dept30;
Role set.
注意:
在Oracle 10g中不赞成使用初始参数MAX_ENABLED_ROLES。保留该参数只是为了和以前的版本兼容。
5. 启用密码的角色
为了增强数据库中的安全性,DBA可以为角色赋予密码。在创建角色时为其赋予密码:
SQL> create role dept99 identified by d99secretpw;
Role created.
SQL> grant dept99 to scott;
Grant succeeded.
SQL> alter user scott default role all except hr_clerk, dept30, dept99;
User altered.
SQL> create role dept99 identified by d99secretpw;
Role created.
SQL> grant dept99 to scott;
Grant succeeded.
SQL> alter user scott default role all except hr_clerk, dept30, dept99;
当用户SCOTT连接到数据库时,他正在使用的应用程序将提供密码或提示用户输入密码,或者他可以在启用角色时输入密码:
SQL> set role dept99 identified by d99secretpw;
Role set.
6. 角色数据字典视图
表109-12列出了与角色相关的数据字典视图。
表109-12 与角色相关的数据字典视图
数据字典视图 | 说 明 |
DBA_ROLES | 所有的角色以及它们是否需要密码 |
DBA_ROLE_PRIVS | 授予用户或其他角色的角色 |
ROLE_ROLE_PRIVS | 授予其他角色的角色 |
ROLE_SYS_PRIVS | 已经授予角色的系统权限 |
ROLE_TAB_PRIVS | 已经授予角色的表权限和表列权限 |
SESSION_ROLES | 当前对该会话有效的角色。可用于每个用户会话 |
视图DBA_ROLE_PRIVS可以很好地用于:找出哪些角色被授予了用户,这些用户是否可以将该角色传递给另一个用户(ADMIN_OPTIONADMIN OPTION),以及该角色是否在默认情况下启用(DEFAULT_ROLE):
SQL> select * from dba_role_privs
2 where grantee = 'SCOTT';
GRANTEE GRANTED_ROLE ADMIN_OPTION DEFAULT_ROLE
----------- -------------------- ------------ ------------
SCOTT DEPT30 NO NO
SCOTT DEPT50 NO YES
SCOTT DEPT99 NO YES
SCOTT CONNECT NO YES
SCOTT HR_CLERK NO NO
SCOTT RESOURCE NO YES
SCOTT ALL_DEPTS NO YES
SCOTT DELETE_CATALOG_ROLE NO YES
8 rows selected.
SQL> select * from dba_role_privs
2 where grantee = 'SCOTT';
GRANTEE GRANTED_ROLE ADMIN_OPTION DEFAULT_ROLE
------------ -------------------- ------------ ------------
SCOTT DEPT30 NO NO
SCOTT DEPT50 NO YES
SCOTT DEPT99 NO YES
SCOTT CONNECT NO YES
SCOTT HR_CLERK NO NO
SCOTT RESOURCE NO YES
SCOTT ALL_DEPTS NO YES
SCOTT DELETE_CATALOG_ROLE NO YES
类似地,可以找出将哪些角色赋予ALL_DEPTS角色:
SQL> select * from dba_role_privs
2> where grantee = 'ALL_DEPTS';
GRANTEE GRANTED_ROLE ADMIN_OPTION DEFAULT_ROLE
------------ ------------------ ------------ ------------
ALL_DEPTS DEPT30 NO YES
ALL_DEPTS DEPT50 NO YES
ALL_DEPTS DEPT100 NO YES
3 rows selected.
SQL> select * from dba_role_privs
2> where grantee = 'ALL_DEPTS';
GRANTEE GRANTED_ROLE ADMIN_OPTION DEFAULT_ROLE
------------ -------------------- ------------ ------------
ALL_DEPTS DEPT30 NO YES
ALL_DEPTS DEPT50 NO YES
ALL_DEPTS DEPT100 NO YES
数据字典视图ROLE_ROLE_PRIVS也可以用于获得这些信息。它只包含有关赋予角色的角色信息,没有DEFAULT_ROLE信息。
为了找出表或表列上授予用户的权限,可以编写2个查询:一个查询用于检索直接授予的权限,另一个查询用于检索通过角色间接授予的权限。检索直接授予的权限非常简单:
SQL> select dtp.grantee, dtp.owner, dtp.table_name,
2 dtp.grantor, dtp.privilege, dtp.grantable
3 from dba_tab_privs dtp
4 where dtp.grantee = 'SCOTT';
GRANTEE OWNER TABLE_NAME GRANTOR PRIVILEGE GRANTABLE
----------- --------- ------------- ---------- ---------- ----------
SCOTT HR EMPLOYEES HR SELECT YES
SCOTT HR EMPLOYEES HR DELETE NO
SCOTT HR EMPLOYEES HR INSERT NO
4 rows selected.
SQL> select dtp.grantee, dtp.owner, dtp.table_name,
2 dtp.grantor, dtp.privilege, dtp.grantable
3 from dba_tab_privs dtp
4 where dtp.grantee = 'SCOTT';
GRANTEE OWNER TABLE_NAME GRANTOR PRIVILEGE GRANTABLE
------------ ---------- --------------- ------------ ------------ ----------
SCOTT HR EMPLOYEES HR SELECT YES
SCOTT HR EMPLOYEES HR DELETE NO
SCOTT HR EMPLOYEES HR INSERT NO
为了检索通过角色授予的表权限,需要连接DBA_ROLE_PRIVS和ROLE_TAB_PRIVS。DBA_ROLE_PRIVS具有赋予用户的角色,而ROLE_TAB_PRIVS具有赋予角色的权限:
SQL> select drp.grantee, rtp.owner, rtp.table_name,
2 rtp.privilege, rtp.grantable, rtp.role
3 from role_tab_privs rtp
4 join dba_role_privs drp on rtp.role = drp.granted_role
5 where drp.grantee = 'SCOTT';
GRANTEE OWNER TABLE_NAME PRIVILEGE GRANTABLE ROLE
--------- ------- -------------- ------------ ---------- ---------------
SCOTT HR EMPLOYEES SELECT NO HR_CLERK
SCOTT HR JOBS SELECT NO JOB_MAINT
SCOTT HR JOBS UPDATE NO JOB_MAINT
SCOTT SYS AUD$ DELETE NO DELETE_CATA
LOG_ROLE
SCOTT SYS FGA_LOG$ DELETE NO DELETE_CATA
LOG_ROLE
5 rows selected.
SQL> select drp.grantee, rtp.owner, rtp.table_name,
2 rtp.privilege, rtp.grantable, rtp.role
3 from role_tab_privs rtp
4 join dba_role_privs drp on rtp.role = drp.granted_role
5 where drp.grantee = 'SCOTT';
GRANTEE OWNER TABLE_NAME PRIVILEGE GRANTABLE ROLE
---------- -------- --------------- ------------ ---------- ---------------
SCOTT HR EMPLOYEES SELECT NO HR_CLERK
SCOTT HR JOBS SELECT NO JOB_MAINT
SCOTT HR JOBS UPDATE NO JOB_MAINT
SCOTT SYS AUD$ DELETE NO DELETE_CATA
LOG_ROLE
SCOTT SYS FGA_LOG$ DELETE NO DELETE_CATA
LOG_ROLE
在SCOTT的权限中,注意他具有HR.EMPLOYEES表上的SELECT权限,该权限不仅直接通过grant命令授予,而且还通过角色授予。取消其中一个权限不会影响SCOTT对HR.EMPLOYEES表的访问,除非同时删除这两个权限。
109.3.5 使用VPD实现应用程序安全策略
虚拟专用数据库(Virtual Private Database(,VPD) 将服务器实施的细粒度访问控制和安全应用程序上下文结合起来。支持上下文的函数返回一个谓词,即where子句,该子句自动附加到所有的select语句 或其他DML语句。换句话说,由VPD控制的表、视图、同义词上的select语句将根据where子句返回行的子集,该子句由通过应用程序上下文生效的 安全策略函数自动生成。VPD的主要组成部分是行级别的安全性(RLS),也称为“细粒度的访问控制”(FGAC)。
因为VPD在语句解析期间透明地生成谓词,因此可以一致地实施安全策略,而无论无论用户是否正在运行特别的查询、检索应用程序中的数据或者查看Oracle Forms中的数据,都可以一致地实施安全策略。因为Oracle Server在解析时将谓词应用于语句,所以应用程序不需要使用特殊的表、视图等来实现该策略。因此,Oracle可以使用索引、物化视图和并行操作来优化查询,而以其他的方式则不能够进行优化。因此,相比于使用应用程序或其他方式过滤结果的查询,使用VPD可能会产生较少的系统开销。
从维护的角度来看,安全策略可以在安全函数中定义, 使用角色和权限很难创建这种安全函数。类似地,应用程序服务器提供商(Application Server Provider,ASP)可能只需要建立一个数据库来为相同应用程序的多个客户服务,使用VPD策略来确保一个顾客的雇员只可以查看他们自己的数据。 DBA可以使用少量的VPD策略维护一个较大的数据库,而不是针对每个客户都使用一个单独的数据库。
Oracle 10g中的新增内容是列级别的VPD操作。使用列级别的VPD,DBA可以约束对表中特定列的访问。查询返回相同数量的行,但如果用户的上下文不允许访问列,则在约束的列中返回NULL值。
VPD策略可以是静态的、上下文相关的或动态的。静态的或上下文相关的VPD策略是Oracle Database 10g的新增内容,它们可以极大地改进性能,因为它们不需要在每次运行查询时调用策略函数,这是由于在会话中将其缓存以方便以后后面的使用。在Oracle Database 10g之前,所有的策略都是动态的。换句话说,每次解析包含目标VPD表的SQL语句时都运行策略函数。每次登录,在注册期间估计静态策略都要评估一次一次,并且在整个会话期间保持缓存,而不考虑应用程序上下文。使用上下文相关的策略时,如果应用程序上下文改变,则在语句解析时调用策略函数:例如,实施“雇员只可以看到他们自己的薪水历史记录,但经理可以看到他们雇员的所有薪水情况”这种业务规则的策略。如果执行语句的雇员没有改变,就不需要再次调用策略函数,从而减少由于VPD策略实施而产生的系统开销量。
可以使用create context命令创建应用程序上下文,并且使用程序包DBMS_RLS管理VPD策略。可以像其他任何函数一样创建用于返回谓词以实施策略的函数,但这种函数具有两个必需的参数,并且返回一个VARCHAR2。本章后面将详细介绍这些函数,并且使用在Oracle数据库安装期间提供的示例模式来创建一个VPD示例。
1. 应用程序上下文
使用create context命令,可以创建应用程序定义的属性的名称,这些属性用于实施安全策略。此外,还可以定义函数和过程的程序包名称,这些函数和过程用于设置用户会话的安全上下文。下面是示例:
create context hr_security using vpd.emp_access;
create or replace package emp_access as
procedure set_security_parameters;
end;
create context hr_security using vpd.emp_access;
create or replace package emp_access as
procedure set_security_parameters;
在该示例中,上下文名称是HR_SECURITY,用于在会话期间为用户建立特征或属性的程序包称为EMP_ACCESS。在登录触发器中调用过程SET_SECURITY_PARAMETERS。因为上下文HR_SECURITY只绑定到EMP_ACCESS,因此没有其他的过程可以改变会话属性。这可以确保产生安全的应用程序上下文,在连接到数据库后用户或任何其他进程都不可以改变安全的应用程序上下文该上下文。
在用于实现应用程序上下文的一般典型程序包中,使用内置的上下文USERENV来检索有关用户会话自身的信息。在表109-13中是USERENV上下文中一些更为常见的参数。
表109-13 常见的USERENV上下文参数
参 数 | 返 回 值 |
CURRENT_SCHEMA | 会话的默认模式 |
DB_NAME | 在初始参数DB_NAME中指定的数据库名称 |
HOST | 用户连接的主机名称 |
IP_ADDRESS | 用户连接的IP地址 |
OS_USER | 初始化数据库会话的操作系统账户 |
SESSION_USER | 经过验证的数据库用户名 |
例如,下面对SYS_CONTEXT的调用将检索数据库会话的用户名和IP_ADDRESS:
declare
username varchar2(30);
ip_addr varchar2(30);
begin
username := SYS_CONTEXT('USERENV','SESSION_USER');
ip_addr := SYS_CONTEXT('USERENV','IP_ADDRESS');
-- other processing here
end;
declare
username varchar2(30);
ip_addr varchar2(30);
begin
username := SYS_CONTEXT('USERENV','SESSION_USER');
ip_addr := SYS_CONTEXT('USERENV','IP_ADDRESS');
-- other processing here
类似地,可以在SQL select语句中使用SYS_CONTEXT函数:
SQL> select SYS_CONTEXT('USERENV','SESSION_USER') username from dual;
USERNAME
-------------------------
KSHELTON
SQL> select SYS_CONTEXT('USERENV','SESSION_USER') username from dual;
USERNAME
-------------------------
使用USERENV上下文和数据库中授权信息的一些组合,可以使用DBMS_SESSION.SET_ CONTEXT,将值赋予所创建的应用程序上下文中的参数:
dbms_session.set_context('HR_SECURITY','SEC_LEVEL','HIGH');
在该示例中,应用程序上下文变量SEC_LEVEL在HR_SECURITY上下文中设置为HIGH。可以根据大量条件来分配该值,包括根据用户ID来分配安全级别的映射表。
为了确保针对每个会话设置上下文变量,可以使用登录触发器来调用与该上下文关联的过程。前面提及,在分配的程序包中只可以设置或改变上下文中的变量。下面是一个示例的登录触发器,该触发器调用过程以建立上下文:
create or replace trigger vpd.set_security_parameters
after logon on database
begin
vpd.emp_access.set_security_parameters;
end;
create or replace trigger vpd.set_security_parameters
after logon on database
begin
vpd.emp_access.set_security_parameters;
在该示例中,过程SET_SECURITY_PARAMETERS将需要调用DBMS_SESSION.SET_ CONTEXT。
在Oracle Enterprise Manager中,可以使用Policy Manager来建立上下文和策略组,如图109-8所示。
图109-8 Oracle Policy Manager
2. 安全策略实现
一旦基础结构到位之后在适当的位置,从而就可以建立安全环境,下一步就是定义用于生成谓词的函数,这些谓词将附加到每个针对受保护表的每个select 语句或DML命令。用于实现谓词生成的函数有2个参数:受保护对象的拥有者、拥有者模式中对象的名称。一个函数只可以处理一种操作类型的谓词生成,例如 select,或者可以适用于所有的DML命令,这取决于该函数如何关联受保护的表。下面的示例显示了包含两个函数的程序包主体:一个函数将用于控制 select语句中的访问,另一个函数将用于任何其他的DML语句:
create or replace package body get_predicates is
function emp_select_restrict(owner varchar2, object_name varchar2)
return varchar2 is
ret_predicate varchar2(1000); -- part of WHERE clause
begin
-- only allow certain employees to see rows in the table
-- . . . check context variables and build predicate
return ret_predicate;
end emp_select_restrict;
function emp_dml_restrict(owner varchar2, object_name varchar2)
return varchar2 is
ret_predicate varchar2(1000); -- part of WHERE clause
begin
-- only allow certain employees to make changes to the table
-- . . . check context variables and build predicate
return ret_predicate;
end emp_dml_restrict;
end; -- package body
create or replace package body get_predicates is
function emp_select_restrict(owner varchar2, object_name varchar2)
return varchar2 is
ret_predicate varchar2(1000); -- part of WHERE clause
begin
-- only allow certain employees to see rows in the table
-- . . . check context variables and build predicate
return ret_predicate;
end emp_select_restrict;
function emp_dml_restrict(owner varchar2, object_name varchar2)
return varchar2 is
ret_predicate varchar2(1000); -- part of WHERE clause
begin
-- only allow certain employees to make changes to the table
-- . . . check context variables and build predicate
return ret_predicate;
end emp_dml_restrict;
每个函数返回一个包含表达式的字符串,该表达式被添加到select语句或DML命令的where子句。用户或应用程序永远不会看到这个WHERE子句的值,它在解析时自动添加到该命令。
开发人员必须确保这些函数总是返回有效的表达式。否则,任何对受保护表的访问将总是会失败,如同下面的示例所示:
SQL> select * from hr.employees;
select * from hr.employees
*
ERROR at line 1:
ORA-28113: policy predicate has error
SQL> select * from hr.employees;
select * from hr.employees
*
ERROR at line 1:
错误消息不会表明谓词是什么,并且所有的用户都无法访问表,直到修正谓词函数。本章后面将介绍关于如何调试谓词函数的技巧。
3. 使用DBMS_RLS
内置的程序包DBMS_RLS包含大量子程序,DBA使用这些子程序维护与表、视图和同义词关联的安全策略。表109-14列出了程序包DBMS_RLS中的子程序。任何需要创建和管理策略的用户都必须被授予程序包SYS.DBMS_RLS上的EXECUTE权限。
表109-14 DBMS_RLS程序包的子程序
子 程 序 | 说 明 |
ADD_POLICY | 将细粒度的访问控制策略添加到对象 |
DROP_POLICY | 删除对象中的FGAC策略 |
REFRESH_POLICY | 重新解析与策略关联的、缓存的所有语句 |
ENABLE_POLICY | 启用或禁用FGAC策略 |
CREATE_POLICY_GROUP | 创建策略组 |
ADD_GROUPED_POLICY | 将策略添加到策略组 |
ADD_POLICY_CONTEXT | 添加当前应用程序的上下文 |
DELETE_POLICY_GROUP | 删除策略组 |
DROP_GROUPED_POLICY | 从策略组中删除一个策略 |
DROP_POLICY_CONTEXT | 删除活动应用程序的上下文 |
ENABLE_GROUPED_POLICY | 启用或禁用组策略 |
DISABLE_GROUPED_POLICY | 禁用组策略 |
REFRESH_GROUPED_POLICY | 重新解析与策略组关联的、缓存的所有语句 |
本章中将介绍最常用的子程序,ADD_POLICY和DROP_POLICY。ADD_POLICY的语法如下:
DBMS_RLS.ADD_POLICY
(
object_schema IN varchar2 null,
object_name IN varchar2,
policy_name IN varchar2,
function_schema IN varchar2 null,
policy_function IN varchar2,
statement_types IN varchar2 null,
update_check IN boolean false,
enable IN boolean true,
static_policy IN boolean false,
policy_type IN binary_integer null,
long_predicate IN in Boolean false,
sec_relevant_cols IN varchar2,
sec_relevant_cols_opt IN binary_integer null
);
DBMS_RLS.ADD_POLICY
(
object_schema IN varchar2 null,
object_name IN varchar2,
policy_name IN varchar2,
function_schema IN varchar2 null,
policy_function IN varchar2,
statement_types IN varchar2 null,
update_check IN boolean false,
enable IN boolean true,
static_policy IN boolean false,
policy_type IN binary_integer null,
long_predicate IN in Boolean false,
sec_relevant_cols IN varchar2,
sec_relevant_cols_opt IN binary_integer null
注意,其中一些参数具有BOOLEAN默认值,并且较少使用的参数都在参数列表的末端。对于绝大多数情况来说,这就使对DBMS_RLS.ADD_POLICY的特定调用的语法更易于编写和理解。表109-15提供了每个参数的说明和用法。
表109-15 DBMS_RLS.ADD_POLICY的参数
参 数 | 说 明 |
object_schema | 包含由策略保护的表、视图或同义词的模式。如果该值是NULL,则使用调用过程的用户的模式 |
object_name | 由策略保护的表、视图或同义词的名称 |
policy_name | 添加到该对象的策略的名称。对于受保护的每个对象,该策略名必须唯一 |
function_schema | 拥有策略函数的模式;如果该值为NULL,则使用调用过程的用户的模式 |
policy_function | 函数名称,该函数为针对object_name的策略生成谓词。如果函数是程序包的一部分,则在此处必须也指定程序包名,用于限定策略函数名 |
statement_types | 应用策略的语句类型。允许的值(以逗号分隔)可以是SELECT、INSERT、UPDATE、DELETE和INDEX的任意组合。默认情况下,除了INDEX之外的所有类型都适用 |
update_check | 对于INSERT或UPDATE类型,该参数是可选项,它默认为FALSE。如果该参数为TRUE,则在检查SELECT或DELETE操作时,则该策略也对INSERT或UPDATE语句也要进行检查该策略 |
enable | 该参数默认为TRUE,表明添加该策略时是否启用它 |
static_policy | 如果该参数为TRUE,该策略为任何访问该对象的人产生相同的谓词字符串,除了SYS用户或具有EXEMPT ACCESS POLICY权限的任何用户。该参数的默认值为FALSE |
policy_type | 如果该值不是NULL,则覆盖static_policy。可允许的值是STATIC、SHARED_STATIC、CONTEXT_SENSITIVE、SHARED_CONTEXT_SENSITIVE和DYNAMIC |
long_predicate | 该参数默认为FALSE。如果它为TRUE,谓词字符串最多可为32K字节长。否则,限制为4000字节 |
sec_relevant_cols | 实施列级别的VPD,这是Oracle 10g的新增内容。只应用于表和视图。在列表中指定受保护的列,使用逗号或空格作为分隔符。该策略只应用于指定的敏感的列位于查询或DML语句中时。默认情况下,所有的列都是受保护的 |
sec_relevant_cols_opt | 允许在列级别VPD过滤查询中的行仍然出现在结果集中,这些行具有为每个敏感列返回的NULL值。该参数的默认值为NULL;如果不是默认值,则必须指定DBMS_RLS.ALL_ROWS,用于显示具有针对敏感列为的NULL值的所有列 |
如果不介意用户是否会看到行的部分内容,其实只是看不到而只关心包含机密信息的列,例如Social Security Number(社会保障号)或薪水情况,则使用参数sec_relevant_cols非常便利。在本章后面的示例中,将根据定义的第一个安全策略对来过滤公司大多数雇员过滤的敏感数据。
在下面的示例中,将名为 EMP_SELECT_RESTRICT的策略应用于表HR.EMPLOYEES。模式VPD拥有策略函数 get_predicates.emp_select_restrict。该策略显式地应用于表上的SELECT语句。然而,将UPDATE_CHECK 设置为TRUE时,在更新行或将行插入到表中时,也会检查update或delete命令:
dbms_rls.add_policy (
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMP_SELECT_RESTRICT',
function_schema => 'VPD',
policy_function => 'get_predicates.emp_select_restrict',
statement_types => 'SELECT',
update_check => TRUE,
enable => TRUE
);
dbms_rls.add_policy (
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMP_SELECT_RESTRICT',
function_schema => 'VPD',
policy_function => 'get_predicates.emp_select_restrict',
statement_types => 'SELECT',
update_check => TRUE,
enable => TRUE
因为没有设置static_policy,它默认为FALSE,这意味着该策略是动态的,并且在每次解析select语句时检查该策略。这是在Oracle Database 10g之前唯一可用的行为。
使用子程序ENABLE_POLICY是临时禁用策略的一种简单方法,并且不需要在以后后面将策略重新绑定到表:
dbms_rls.enable_policy(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMP_SELECT_RESTRICT',
enable => FALSE
);
dbms_rls.enable_policy(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMP_SELECT_RESTRICT',
enable => FALSE
如果为相同的对象指定多个策略,则在每个谓词之间添加AND条件。如果需要在多个策略的谓词之间使用OR条件,则很可能需要修订策略。每个策略的逻辑需要整结合到在一个策略中,该策略在谓词的每个部分之间具有OR条件。
4. 创建VPD
本节将从头到尾遍历VPD的完整实现,该示例依赖于和Oracle Database 10g和11g一起安装的示例模式。具体来说,将实现HR.EMPLOYEES表上的FGAC策略,用于根据经理状态地位和雇员的部门编号来限制访问。如果是雇员,可以在HR.EMPLOYEES中看到自己的行;如果是经理,则可以看到他直接管理直接向该经理报告的所有雇员的行。
提示:
如果没有在数据库中安装示例模式,可以使用$ORACLE_HOME/demo/schema中的脚本创建它们。
一旦示例模式到位之后在适当的位置,需要在数据库中创建一些用户,他们想要查看希望看到表HR.EMPLOYEES中的行的用户:
create user smavris identified by smavris702;
grant connect, resource to smavris;
create user dgrant identified by dgrant507;
grant connect, resource to dgrant;
create user kmourgos identified by kmourgos622;
grant connect, resource to kmourgos;
create user smavris identified by smavris702;
grant connect, resource to smavris;
create user dgrant identified by dgrant507;
grant connect, resource to dgrant;
create user kmourgos identified by kmourgos622;
用户KMOURGOS是所有存货管理员的经理,而DGRANT是KMOURGOS的一个雇员。用户SMAVRIS是公司的HR_REP。
在下面的3个步骤中,将HR.EMPLOYEES表上的SELECT权限授予数据库中的每个人,并且将创建一个查找表,将雇员ID号映射到他们的数据库账户。设置用户会话上下文变量的过程将使用该表把雇员ID号赋予上下文变量,在策略函数中将使用该上下文变量来生成谓词。
grant select on hr.employees to public;
create table hr.emp_login_map (employee_id, login_acct)
as select employee_id, email from hr.employees;
grant select on hr.emp_login_map to public;
grant select on hr.employees to public;
create table hr.emp_login_map (employee_id, login_acct)
as select employee_id, email from hr.employees;
接下来,创建称为VPD的用户账户,该账户具有创建上下文和维护策略函数的权限:
create user vpd identified by vpd439;
grant connect, resource, create any context, create public synonym to vpd;
create user vpd identified by vpd439;
连接到VPD模式,创建称为HR_SECURITY的上下文,并且定义用于设置应用程序上下文的程序包和过程:
connect vpd/vpd439@dw;
create context hr_security using vpd.emp_access;
create or replace package vpd.emp_access as
procedure set_security_parameters;
end;
connect vpd/vpd439@dw;
create context hr_security using vpd.emp_access;
create or replace package vpd.emp_access as
procedure set_security_parameters;
记住,程序包VPD.EMP_ACCESS中的过程是可以设置上下文变量的唯一过程。VPD.EMP_ACCESS的程序包主体如下:
create or replace package body vpd.emp_access is
--
-- At user login, run set_security_parameters to
-- retrieve the user login name, which corresponds to the EMAIL
-- column in the table HR.EMPLOYEES.
--
-- context USERENV is pre-defined for user characteristics such
-- as username, IP address from which the connection is made,
-- and so forth.
--
-- for this procedure, we are only using SESSION_USER
-- from the USERENV context.
--
procedure set_security_parameters is
emp_id_num number;
emp_login varchar2(50);
begin
-- database username corresponds to email address in HR.EMPLOYEES
emp_login := sys_context('USERENV','SESSION_USER');
dbms_session.set_context('HR_SECURITY','USERNAME',emp_login);
-- get employee id number, so manager rights can be established
-- but don't bomb out other DB users who are not in the
-- EMPLOYEES table
begin
select employee_id into emp_id_num
from hr.emp_login_map where login_acct = emp_login;
dbms_session.set_context('HR_SECURITY','EMP_ID',emp_id_num);
exception
when no_data_found then
dbms_session.set_context('HR_SECURITY','EMP_ID',0);
end;
-- Future queries will restrict rows based on emp_id
end; -- procedure
end; -- package body
create or replace package body vpd.emp_access is
--
-- At user login, run set_security_parameters to
-- retrieve the user login name, which corresponds to the EMAIL
-- column in the table HR.EMPLOYEES.
--
-- context USERENV is pre-defined for user characteristics such
-- as username, IP address from which the connection is made,
-- and so forth.
--
-- for this procedure, we are only using SESSION_USER
-- from the USERENV context.
--
procedure set_security_parameters is
emp_id_num number;
emp_login varchar2(50);
begin
-- database username corresponds to email address in HR.EMPLOYEES
emp_login := sys_context('USERENV','SESSION_USER');
dbms_session.set_context('HR_SECURITY','USERNAME',emp_login);
-- get employee id number, so manager rights can be established
-- but don't bomb out other DB users who are not in the
-- EMPLOYEES table
begin
select employee_id into emp_id_num
from hr.emp_login_map where login_acct = emp_login;
dbms_session.set_context('HR_SECURITY','EMP_ID',emp_id_num);
exception
when no_data_found then
dbms_session.set_context('HR_SECURITY','EMP_ID',0);
end;
-- Future queries will restrict rows based on emp_id
end; -- procedure
有关该过程还需要注意一些事情。我们通过查询USERENV上下文来检索用户的模式,默认情况下为所有的用户自动启用该上下文。然后,将该模式赋给新创建的上下文HR_SECURITY中的变量USERNAME。通过在映射表HR.EMP_LOGIN_MAP中进行查找来确定另一个HR_SECURITY上下文变量EMP_ID。不希望该过程在已注册登录的用户不在映射表中时中断,并且显示一个错误。相反,分配EMP_ID的值为0,结果就是在策略函数中生成谓词时没有对HR.EMPLOYEES表的任何访问。
在下面的步骤中,授予数据库中每个人程序包上的EXECUTE权限,并且为其创建一个同义词,从而在每次需要调用它时节省一些击键次数:
grant execute on vpd.emp_access to PUBLIC;
create public synonym emp_access for vpd.emp_access;
grant execute on vpd.emp_access to PUBLIC;
为了确保在每个用户登录时为其定义上下文,我们将以SYSTEM身份连接到数据库作为SYS连接,并且创建一个登录触发器,用于在上下文中建立设置变量:
connect system/nolongermanager@dw as sysdba;
create or replace trigger vpd.set_security_parameters
after logon on database
begin
vpd.emp_access.set_security_parameters;
end;
connect sys/sys727@dw as sysdba;
create or replace trigger vpd.set_security_parameters
after logon on database
begin
vpd.emp_access.set_security_parameters;
因为为每个连接到数据库的用户激活该触发器,所以如果不是为每个用户测试代码的话,则极其重要的方面是为每一类的用户测试代码就极其重要,如果没有为数据库中的每个用户激活该触发器的话!如果触发器失败并且显示一个错误,那么非SYSDBA的常规用户将无法注册登录。
到目前为止,已经有了定义的了上下文、用来设置建立上下文变量的过程以及自动调用该过程的触发器。作为前面定义的3个用户中的一个进行登录,可以查询上下文的内容:
SQL> connect smavris/smavris702@dw
Connected.
SQL> select * from session_context;
NAMESPACE ATTRIBUTE VALUE
------------------------ ------------------------- ---------------------
HR_SECURITY USERNAME SMAVRIS
HR_SECURITY EMP_ID 203
2 rows selected.
SQL> connect smavris/smavris702@dw
Connected.
SQL> select * from session_context;
NAMESPACE ATTRIBUTE VALUE
------------------------ ------------------------- ---------------------
HR_SECURITY USERNAME SMAVRIS
HR_SECURITY EMP_ID 203
注意当SMAVRIS尝试冒充另一个雇员时发生的情况:
SQL> begin
2 dbms_session.set_context('HR_SECURITY','EMP_ID',100);
3 end;
begin
*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SYS.DBMS_SESSION", line 94
ORA-06512: at line 2
SQL> begin
2 dbms_session.set_context('HR_SECURITY','EMP_ID',100);
3 end;
begin
*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SYS.DBMS_SESSION", line 82
只允许程序包VPD.EMP_ACCESS设置或改变上下文中的变量。
最后的步骤包括定义将生成谓词的过程,以及将其中的一个或多个这些过程赋给HR.EMPLOYEES表。作为用户VPD,该用户已经拥有了上下文过程,接下来建立定义谓词的程序包:
connect vpd/vpd439@dw;
create or replace package vpd.get_predicates as
-- note -- security function ALWAYS has two parameters,
-- table owner name and table name
function emp_select_restrict
(owner varchar2, object_name varchar2) return varchar2;
-- other functions can be written here for INSERT, DELETE, and so forth.
end get_predicates;
create or replace package body vpd.get_predicates is
function emp_select_restrict
(owner varchar2, object_name varchar2) return varchar2 is
ret_predicate varchar2(1000); -- part of WHERE clause
begin
-- only allow employee to see their row or immediate subordinates
ret_predicate := 'EMPLOYEE_ID = ' ||
sys_context('HR_SECURITY','EMP_ID') ||
' OR MANAGER_ID = ' ||
sys_context('HR_SECURITY','EMP_ID');
return ret_predicate;
end emp_select_restrict;
end; -- package body
connect vpd/vpd439@dw;
create or replace package vpd.get_predicates as
-- note: security function ALWAYS has two parameters,
-- table owner name and table name
function emp_select_restrict
(owner varchar2, object_name varchar2) return varchar2;
-- other functions can be written here for INSERT, DELETE, and so forth.
end get_predicates;
create or replace package body vpd.get_predicates is
function emp_select_restrict
(owner varchar2, object_name varchar2) return varchar2 is
ret_predicate varchar2(1000); -- part of WHERE clause
begin
-- only allow employee to see their row or immediate subordinates
ret_predicate := 'EMPLOYEE_ID = ' ||
sys_context('HR_SECURITY','EMP_ID') ||
' OR MANAGER_ID = ' ||
sys_context('HR_SECURITY','EMP_ID');
return ret_predicate;
end emp_select_restrict;
一旦使用DBMS_RLS将该函数附加到表,将生成一个文本字符串,在每次访问表时将该文本字符串用于WHERE子句中。该字符串总是类似于:
EMPLOYEE_ID = 124 OR MANAGER_ID = 124
和建立上下文环境的程序包一样,需要允许用户访问该程序包:
grant execute on vpd.get_predicates to PUBLIC;
create public synonym get_predicates for vpd.get_predicates;
grant execute on vpd.get_predicates to PUBLIC;
最后(但并不是最不重要的方面),使用DBMS_RLS.ADD_POLICY过程将策略函数附加 到表:
dbms_rls.add_policy (
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMP_SELECT_RESTRICT',
function_schema => 'VPD',
policy_function => 'get_predicates.emp_select_restrict',
statement_types => 'SELECT',
update_check => TRUE,
enable => TRUE
);
dbms_rls.add_policy (
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMP_SELECT_RESTRICT',
function_schema => 'VPD',
policy_function => 'get_predicates.emp_select_restrict',
statement_types => 'SELECT',
update_check => TRUE,
enable => TRUE
雇员可以像前面一样访问HR.EMPLOYEES表,但他们将只能看到自己的行,以及为其工作的雇员的行(如果存在这种雇员的话)。作为KMOURGOS注册登录,尝试检索HR.EMPLOYEES表的所有行,但只可以看到KMOURGOS的行以及他直接管理直接向其报告的雇员的行:
SQL> connect kmourgos/kmourgos622@dw;
Connected.
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ---------- ---------- --------- --------- ------- ----------
124 Kevin Mourgos KMOURGOS ST_MAN 5800 100
141 Trenna Rajs TRAJS ST_CLERK 3500 124
142 Curtis Davies CDAVIES ST_CLERK 3100 124
143 Randall Matos RMATOS ST_CLERK 2600 124
144 Peter Vargas PVARGAS ST_CLERK 2500 124
196 Alana Walsh AWALSH SH_CLERK 3100 124
197 Kevin Feeney KFEENEY SH_CLERK 3000 124
198 Donald OConnell DOCONNEL SH_CLERK 2600 124
199 Douglas Grant DGRANT SH_CLERK 2600 124
9 rows selected.
SQL> connect kmourgos/kmourgos622@dw;
Connected.
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ---------- ----------- ---------- ---------- ------- ----------
124 Kevin Mourgos KMOURGOS ST_MAN 5800 100
141 Trenna Rajs TRAJS ST_CLERK 3500 124
142 Curtis Davies CDAVIES ST_CLERK 3100 124
143 Randall Matos RMATOS ST_CLERK 2600 124
144 Peter Vargas PVARGAS ST_CLERK 2500 124
196 Alana Walsh AWALSH SH_CLERK 3100 124
197 Kevin Feeney KFEENEY SH_CLERK 3000 124
198 Donald OConnell DOCONNEL SH_CLERK 2600 124
199 Douglas Grant DGRANT SH_CLERK 2600 124
对于用户DGRANT,情况则完全不同:
SQL> connect dgrant/dgrant507@dw;
Connected.
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ---------- --------- -------- --------- ------- ----------
199 Douglas Grant DGRANT SH_CLERK 2600 124
1 row selected.
SQL> connect dgrant/dgrant507@dw;
Connected.
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ---------- ----------- ---------- ---------- ------- ----------
199 Douglas Grant DGRANT SH_CLERK 2600 124
DGRANT开始只能看到自己的行,因为他不管理公司内的其他任何人。
在SMAVRIS的情况中,可以通过查询看到类似的结果:
SQL> connect smavris/smavris702@dw;
Connected.
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ---------- ------------ ---------- ---------- ------- ----------
203 Susan Mavris SMAVRIS HR_REP 6500 101
1 row selected.
SQL> connect smavris/smavris702@dw;
Connected.
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ---------- ------------ ---------- ---------- ------- ----------
203 Susan Mavris SMAVRIS HR_REP 6500 101
但是需要注意,SMAVRIS属于人力资源(HR)部门,所以应 该能够看到表中的所有行。此外,SMAVRIS应该是可以查看所有雇员薪水信息的唯一的人。因此,需要改变策略函数,为SMAVRIS和HR部门中的其他 雇员提供对HR.EMPLOYEES表的完全访问。此外,可以使用策略赋值中列级别的约束来返回相同数量的行,但其中的敏感数据作为NULL值返回。
为了方便HR部门的雇员对HR.EMPLOYEES表的访问,首先需要改变映射表,使其包括JOB_ID列。如果JOB_ID列的值是具有HR_REP的值,该雇员就属于HR部门。首先禁止该策略生效,并且创建新的映射表:
SQL> begin
2 dbms_rls.enable_policy(
3 object_schema => 'HR',
4 object_name => 'EMPLOYEES',
5 policy_name => 'EMP_SELECT_RESTRICT',
6 enable => FALSE
7 );
8 end;
PL/SQL procedure successfully completed.
SQL> drop table hr.emp_login_map;
Table dropped.
SQL> create table hr.emp_login_map (employee_id, login_acct, job_id)
2 as select employee_id, email, job_id from hr.employees;
Table created.
SQL> grant select on hr.emp_login_map to public;
Grant succeeded.
SQL> begin
2 dbms_rls.enable_policy(
3 object_schema => 'HR',
4 object_name => 'EMPLOYEES',
5 policy_name => 'EMP_SELECT_RESTRICT',
6 enable => FALSE
7 );
8 end;
PL/SQL procedure successfully completed.
SQL> drop table hr.emp_login_map;
Table dropped.
SQL> create table hr.emp_login_map (employee_id, login_acct, job_id)
2 as select employee_id, email, job_id from hr.employees;
Table created.
SQL> grant select on hr.emp_login_map to public;
用于建立设置上下文变量的过程VPD.EMP_ACCESS需要添加另一个上下文变量,该上下文变量表明访问表的用户的安全级别。改变SELECT语句,并且对DBMS_SESSION.SET_CONTEXT进行另一次调用,如下所示:
. . .
emp_job_id varchar2(50);
. . .
select employee_id, job_id into emp_id_num, emp_job_id
from hr.emp_login_map where login_acct = emp_login;
dbms_session.set_context('HR_SECURITY','SEC_LEVEL',
case emp_job_id when 'HR_REP' then 'HIGH' else 'NORMAL' end );
. . .
. . .
emp_job_id varchar2(50);
. . .
select employee_id, job_id into emp_id_num, emp_job_id
from hr.emp_login_map where login_acct = emp_login;
dbms_session.set_context('HR_SECURITY','SEC_LEVEL',
case emp_job_id when 'HR_REP' then 'HIGH' else 'NORMAL' end );
当雇员具有HR_REP的职称职位时,将上下文变量SEC_LEVEL设置为HIGH而不是NORMAL。在策略函数中,需要检查这个新的条件,如下所示:
create or replace package body vpd.get_predicates is
function emp_select_restrict
(owner varchar2, object_name varchar2) return varchar2 is
ret_predicate varchar2(1000); -- part of WHERE clause
begin
-- only allow employee to see their row or immediate subordinates,
-- unless they have high security clearance
if sys_context('HR_SECURITY','SEC_LEVEL') = 'HIGH' then
ret_predicate := ''; -- no restrictions in WHERE clause
else
ret_predicate := 'EMPLOYEE_ID = ' ||
sys_context('HR_SECURITY','EMP_ID') ||
' OR MANAGER_ID = ' ||
sys_context('HR_SECURITY','EMP_ID');
end if;
return ret_predicate;
end emp_select_restrict;
end; -- package body
create or replace package body vpd.get_predicates is
function emp_select_restrict
(owner varchar2, object_name varchar2) return varchar2 is
ret_predicate varchar2(1000); -- part of WHERE clause
begin
-- only allow employee to see their row or immediate subordinates,
-- unless they have high security clearance
if sys_context('HR_SECURITY','SEC_LEVEL') = 'HIGH' then
ret_predicate := ''; -- no restrictions in WHERE clause
else
ret_predicate := 'EMPLOYEE_ID = ' ||
sys_context('HR_SECURITY','EMP_ID') ||
' OR MANAGER_ID = ' ||
sys_context('HR_SECURITY','EMP_ID');
end if;
return ret_predicate;
end emp_select_restrict;
因为策略是动态的,因此每次执行SELECT语句时都生成谓词,从而不需要进行策略刷新。当用户SMAVRIS,即HR部门的代表,现在运行查询时,可以看到HR.EMPLOYEES表中的所有行:
SQL> connect smavris/smavris702@dw;
Connected.
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ----------- --------- --------- ---------- ------- ----------
100 Steven King SKING AD_PRES 24000
101 Neena Kochhar NKOCHHAR AD_VP 17000 100
. . .
204 Hermann Baer HBAER PR_REP 10000 101
205 Shelley Higgins SHIGGINS AC_MGR 12000 101
206 William Gietz WGIETZ AC_ACCOUNT 8300 205
107 rows selected.
SQL> connect smavris/smavris702@dw;
Connected.
SQL> select employee_id, first_name, last_name,
2> email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ----------- ----------- ---------- ---------- ------- ----------
100 Steven King SKING AD_PRES 24000
101 Neena Kochhar NKOCHHAR AD_VP 17000 100
. . .
204 Hermann Baer HBAER PR_REP 10000 101
205 Shelley Higgins SHIGGINS AC_MGR 12000 101
206 William Gietz WGIETZ AC_ACCOUNT 8300 205
SMAVRIS在HR_SECURITY上下文中的安全级别是HIGH:
SQL> connect smavris/smavris702
Connected.
SQL> select sys_context('HR_SECURITY','SEC_LEVEL') from dual;
SYS_CONTEXT('HR_SECURITY','SEC_LEVEL')
--------------------------------------------------------------
HIGH
SQL>
然而,DGRANT仍然只可以看到表中自己的行,因为他在HR_SECURITY上下文中的安全级别是NORMAL:
SQL> connect dgrant/dgrant507@dw;
Connected.
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ---------- ---------- --------- ---------- ------- ----------
199 Douglas Grant DGRANT SH_CLERK 2600 124
1 row selected.
SQL> select sys_context('HR_SECURITY','SEC_LEVEL') from dual;
SYS_CONTEXT('HR_SECURITY','SEC_LEVEL')
--------------------------------------------------------------
NORMAL
SQL> connect dgrant/dgrant507@dw;
Connected.
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ---------- ----------- ---------- ---------- ------- ----------
199 Douglas Grant DGRANT SH_CLERK 2600 124
为了实施只有HR雇员可以看到薪水信息的需求,需要稍微修改策略函数,启用具有列级别约束的策略:
dbms_rls.add_policy (
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMP_SELECT_RESTRICT',
function_schema => 'VPD',
policy_function => 'get_predicates.emp_select_restrict',
statement_types => 'SELECT',
update_check => TRUE,
enable => TRUE,
sec_relevant_cols => 'SALARY',
sec_relevant_cols_opt => dbms_rls.all_rows
);
dbms_rls.add_policy (
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMP_SELECT_RESTRICT',
function_schema => 'VPD',
policy_function => 'get_predicates.emp_select_restrict',
statement_types => 'SELECT',
update_check => TRUE,
enable => TRUE,
sec_relevant_cols => 'SALARY',
sec_relevant_cols_opt => dbms_rls.all_rows
最后一 个参数SEC_RELEVANT_COLS_OPT指定程序包常量DBMS_RLS.ALL_ROWS,用于表明仍然希望看到查询结果中的所有行,但是具 有返回NULL值的相关列(在当前情况中是SALARY)。否则,将不会看到包含SALARY列的查询中的任何行。
5. 调试VPD策略
即使没有获得“ORA-28113: policy predicate has error”或“ORA-00936: missing expression”,查看语句解析时生成的实际谓词也会非常有用。有两种方法可调试谓词,这两种方法都各有其优点和缺点。
第一种方法使用动态性能视图V$SQLAREA和V$VPD_POLICY。顾名思义如同其名称所暗示的那样,V$SQLAREA包含当前位于共享池中的SQL语句,以及当前的执行统计。视图V$VPD_POLICY列出当前在数据库中实施的所有策略,以及谓词。连接2个表,如同下面的示例所示,连接2个表,可以提供一些信息,我们需要通过这些信息来帮助调试在查询结果中遇到的任何问题:
SQL> select s.sql_text, v.object_name, v.policy, v.predicate
2 from v$sqlarea s, v$vpd_policy v
3 where s.hash_value = v.sql_hash;
SQL_TEXT OBJECT_NAM POLICY PREDICATE
---------------------- ---------- ------------------- -----------------
select employee_id, first EMPLOYEES EMP_SELECT_RESTRICT EMPLOYEE_ID = 199
_name, last_name, email, OR MANAGER_ID = 199
job_id, salary, manager_i
d from hr.employees
select employee_id, first EMPLOYEES EMP_SELECT_RESTRICT
_name, last_name, email,
job_id, salary, manager_i
d from hr.employees
SQL>
SQL> select s.sql_text, v.object_name, v.policy, v.predicate
2 from v$sqlarea s, v$vpd_policy v
3 where s.hash_value = v.sql_hash;
SQL_TEXT OBJECT_NAME POLICY PREDICATE
-------------------- ------------ -------------------- -----------------
select employee_id, EMPLOYEES EMP_SELECT_RESTRICT
first_name, last_nam
e, email, job_i
d, salary, manager_i
d from hr.employees
select employee_id, EMPLOYEES EMP_SELECT_RESTRICT EMPLOYEE_ID = 124
first_name, last_nam OR MANAGER_ID =
e, email, job_i 124
d, salary, manager_i
d from hr.employees
select employee_id, EMPLOYEES EMP_SELECT_RESTRICT EMPLOYEE_ID = 199
first_name, last_nam OR MANAGER_ID =
e, email, job_i 199
d, salary, manager_i
d from hr.employees
如果在此查询中将连接添加一个到该查询中的V$SESSION的连接,则可以识别标识哪个用户正在运行SQL。这在第二个SQL语句中尤其重要,此SQL语句没有应用谓词,因此,我们能够推断的只是HR雇员之一运行此查询。该方法的不足之处在于:如果数据库非常忙,则在有机会运行该查询之前,可能由于其他的SQL命令而在共享池中刷新了当前SQL命令。
另一个方法使用alter session命令来生成纯文本的跟踪文件,该文件包含前面查询的许多信息。下面是建立跟踪的命令:
SQL> begin
2 dbms_rls.refresh_policy;
3 end;
PL/SQL procedure successfully completed.
SQL> alter session set events '10730 trace name context forever, level 12';
Session altered.
SQL> begin
2 dbms_rls.refresh_policy;
3 end;
PL/SQL procedure successfully completed.
SQL> alter session set events '10730 trace name context forever, level 12';
为跟踪RLS策略谓词定义事件10730。其他可以跟踪的常见事件是用于会话登录/退出的10029和10030、用于跟踪位图索引访问的10710、用于模仿重做日志的写入错误的10253,以及其他事件。一旦改变会话,用户DGRANT运行其查询:
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ----------- --------- ---------- ---------- ------- ----------
199 Douglas Grant DGRANT SH_CLERK 2600 124
1 row selected.
SQL> select employee_id, first_name, last_name,
2 email, job_id, salary, manager_id from hr.employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL JOB_ID SALARY MANAGER_ID
----------- ----------- ----------- ---------- ---------- ------- ----------
199 Douglas Grant DGRANT SH_CLERK 2600 124
下面查看跟踪文件底部的内容,该跟踪文件位于由初始参数USER_DUMP_DEST指定的目录中(在Oracle Database 11g中由DIAGNOSTIC_DEST参数指定):
Trace file
/u01/app/oracle/diag/rdbms/dw/dw/trace/dw_ora_31128.trc
Oracle Database 11g Enterprise Edition
Release 11.1.0.6.0 ¨C Productio
With the Partitioning, OLAP, Data Mining and
Real Application Testing options
ORACLE_HOME = /u01/app/oracle/product/11.1.0/db_1
System name: Linux
Node name: dw
Release: 2.6.9-55.0.2.0.1.EL
Version: #1 Mon Jun 25 14:24:38 PDT 2007
Machine: i686
Instance name: dw
Redo thread mounted by this instance: 1
Oracle process number: 40
Unix process pid: 31128, image: oracle@dw (TNS V1-V3)
*** 2007-08-12 12:48:37.852
*** SESSION ID:(120.9389) 2007-08-12 12:48:37.852
*** CLIENT ID:() 2007-08-12 12:48:37.852
*** SERVICE NAME:(SYS$USERS) 2007-08-12 12:48:37.852
*** MODULE NAME:(SQL*Plus) 2007-08-12 12:48:37.852
*** ACTION NAME:() 2007-08-12 12:48:37.852
-------------------------------------------------------------
Logon user : DGRANT
Table/View : HR.EMPLOYEES
Policy name : EMP_SELECT_RESTRICT
Policy function: VPD.GET_PREDICATES.EMP_SELECT_RESTRICT
RLS view :
SELECT "EMPLOYEE_ID","FIRST_NAME","LAST_NAME",
"EMAIL","PHONE_NUMBER",
"HIRE_DATE","JOB_ID","SALARY","COMMISSION_PCT","MANAGER_ID",
"DEPARTMENT_ID" FROM "HR"."EMPLOYEES"
"EMPLOYEES" WHERE (EMPLOYEE_ID = 199 OR MANAGER_ID = 199)
-------------------------------------------------------------/u01/app/oracle/admin/dw/udump/dw_ora_32530.trc
. . .
*** MODULE NAME:(SQL*Plus) 2004-02-19 20:50:38.132
*** SERVICE NAME:(SYS$USERS) 2004-02-19 20:50:38.132
*** SESSION ID:(265.44) 2004-02-19 20:50:38.132
-------------------------------------------------------------
Logon user : DGRANT
Table/View : HR.EMPLOYEES
Policy name : EMP_SELECT_RESTRICT
Policy function : VPD.GET_PREDICATES.EMP_SELECT_RESTRICT
RLS view :
SELECT "EMPLOYEE_ID","FIRST_NAME","LAST_NAME","EMAIL",
"PHONE_NUMBER","HIRE_DATE","JOB_ID","SALARY","COMMISSION_PCT",
"MANAGER_ID","DEPARTMENT_ID"
FROM "HR"."EMPLOYEES" "EMPLOYEES"
WHERE (EMPLOYEE_ID = 199 OR MANAGER_ID = 199)
用户的初始SQL语句以及附加的谓词都清楚地显示在跟踪文件中。使用这种方法的不利方面在于,虽然用户可能也许能够访问动态性能视图,但开发人员通常可能没有访问对服务器自身上的用户转储目录的访问权。因此,在尝试调整谓词问题时可能需要DBA的参与。
确保在完成调试时关闭跟踪,这样可以减少与跟踪操作关联的系统开销和磁盘空间(否则只能退出系统):
SQL> alter session set events '10730 trace name context off';
Session altered.
109.4 审计
Oracle使用大量不同的审计方法来监控使用何种权限,以及访问哪些对象。审计不会防止使用这些权限,但可以提供有用的信息,用于揭示权限的滥用和误用。
表109-16中总结了Oracle数据库中不同类型的审计。
表109-16 审 计 类 型
审 核计 类 型 | 说 明 |
语句审计 | 按照语句类型审计SQL语句,而不论访问何种特定的模式对象。也可以在数据库中指定一个或多个用户,针对特定的语句审计这些用户 |
权限审计 | 审计系统权限,例如CREATE TABLE或ALTER INDEX。和语句审计一样,权限审计可以指定一个或多个特定的用户作为审计的目标 |
模式对象审计 | 审计对特定模式对象上运行起作用的特定语句(例如,DEPARTMENTS表上的UPDATE语句)。模式对象审计总是应用于数据库中的所有用户 |
细粒度的审计 | 根据访问对象的内容来审计表访问和权限。使用程序包DBMS_FGA来建立特定表上的策略 |
下面几节中将回顾介绍DBA如何管理系统和对象权限使用的审计。当需要一定的粒度时,DBA可以使用细粒度的审计来监控对表中某些行或列的访问,而不仅仅是是否访问表。
109.4.1 审计位置
审计记录可以发送到SYS.AUD$数据库表或操作系统文件。为了启用审计并指定记录审计记录的位置,将初始参数AUDIT_TRAIL设置为如下4几个值之一中的一个:
参 数 值 | 动 作 |
NONE, FALSE | 禁用审计 |
OS | 启用审计,将审计记录发送到操作系统文件 |
DB, TRUE | 启用审计,将审计记录发送到SYS.AUD$表 |
DB_EXTENDED | 启用审计,将审计记录发送到SYS.AUD$表,并在CLOB列SQLBIND和SQLTEXT中记录额外的信息 |
XML | 启用审计,以XML格式写所有审计记录 |
EXTENDED | 启用审计,在审计跟踪中记录所有列,包括SqlText和SqlBind的值 |
参数 AUDIT_TRAIL不是动态的,为了使AUDIT_TRAIL参数中的改动生效,必须关闭数据库并重新启动。在对SYS.AUD$表进行审计时,应该 注意监控该表的大小,以避免影响SYS表空间中其他对象的空间需求。推荐周期性归档SYS.AUD$中的行,并且截取该表。Oracle提供了角色 DELETE_CATALOG_ROLE,和批处理工作作业中的特殊账户一起使用,用于归档和截取审计表。
109.4.2 语句审计
所有类型的审计都使用audit命令来打开审计,并且使用noaudit命令来关闭审计。对于语句审计,audit命令的格形式看起来如下所示:
AUDIT sql_statement_clause BY {SESSION | ACCESS}
WHENEVER [NOT] SUCCESSFUL;
AUDIT sql_statement_clause BY {SESSION | ACCESS}
sql_statement_clause包含很多条不同的信息大量不同的信息块,例如希望审计的SQL语句类型以及审计什 么人。
此外,希望在每次动作发生时都对其进行审计(by access)或者只审计一次(by session)。默认是by session。
有时希望审计成功的动作:没有生成错误消息的语句。对于这些语句,添加whenever successful。其他一些时候而有时只关心使用审计语句的命令是否失败,失败原因是、这种失败是由于权限违犯、用完表空间中的空间还是或者语法错误。对于这些情况,使用whenever not successful。
对于大多数类别的审计方法,如果确实希望审计所有类型的表访问或某个用户的任何权限,则可以指定all而不是单个的语句类型或对象。
表109-17列出了可以审计的语句类型,并且在每个类别中包含了相关语句的简要描述。如果指定all,则审计该列表中的任何语句。然而,表9-18中的语句类型在启用审计时不属于all类别;必须在audit命令中显式地指定它们。
表109-17 包括在ALL类别中的可审计语句
语 句 选 项 | SQL操作 |
ALTER SYSTEM | 所有ALTER SYSTEM选项,例如,动态改变实例参数,切换到下一个日志文件组,以及终止用户会话 |
CLUSTER | CREATE、ALTER、DROP或TRUNCATE集群 |
CONTEXT | CREATE CONTEXT或DROP CONTEXT |
DATABASE LINK | CREATE或DROP数据库链接 |
DIMENSION | CREATE、ALTER或DROP维数 |
DIRECTORY | CREATE或DROP目录 |
INDEX | CREATE、ALTER或DROP索引 |
MATERIALIZED VIEW | CREATE、ALTER或DROP物化视图 |
NOT EXISTS | 由于不存在的引用对象而造成的SQL语句的失败 |
PROCEDURE | CREATE或DROP FUNCTION、LIBRARY、PACKAGE、PACKAGE BODY或PROCEDURE |
PROFILE | CREATE、ALTER或DROP配置文件 |
PUBLIC DATABASE LINK | CREATE或DROP公有数据库链接 |
PUBLIC SYNONYM | CREATE或DROP公有同义词 |
ROLE | CREATE、ALTER、DROP或SET角色 |
ROLLBACK SEGMENT | CREATE、ALTER或DROP回滚段 |
SEQUENCE | CREATE或DROP序列 |
SESSION | 登录和退出 |
SYNONYM | CREATE或DROP同义词 |
SYSTEM AUDIT | 系统权限的AUDIT或NOAUDIT |
SYSTEM GRANT | GRANT或REVOKE系统权限和角色 |
TABLE | CREATE、DROP或TRUNCATE表 |
TABLESPACE | CREATE、ALTER或DROP表空间 |
TRIGGER | CREATE、ALTER(启用/禁用)、DROP触发器;具有ENABLE ALL TRIGGERS或DISABLE ALL TRIGGERS的ALTER TABLE |
TYPE | CREATE、ALTER和DROP类型以及类型主体 |
USER | CREATE、ALTER或DROP用户 |
VIEW | CREATE或DROP视图 |
一些示例可以帮助读者更清楚地了解所有这些选项。在示例数据库中,用户SCOTT具有HR模式和其他模式中表上的所有权限。允许SCOTT创建其中一些表上的索引,但万一有一些与执行计划改动相关的性能问题,则需要知道何时创建这些索引。可以使用如下命令审计SCOTT的索引创建:
SQL> audit index by scott whenever successful;
Audit succeeded.
后面的某一天,SCOTT创建了HR.JOBS表上的索引:
SQL> create index job_title_idx on hr.jobs(job_title);
Index created.
检查数据字典视图DBA_AUDIT_TRAIL中的审计跟踪,可以看到SCOTT实际上在2月24日的9:21 P.M.创建了索引:
SQL> select username, to_char(timestamp,'MM/DD/YY HH24:MI') Timestamp,
2 obj_name, action_name, sql_text from dba_audit_trail
3 where username = 'SCOTT';
USERNAME TIMESTAMP OBJ_NAME ACTION_NAME SQL_TEXT
---------- -------------- ---------------- ---------------- ---------------
SCOTT 02/24/04 21:24 JOB_TITLE_IDX CREATE INDEX create index j
ob_title_idx on
hr.jobs(job_ti
tle)
1 row selected.
表109-18 显式指定的语句类型
语 句 选 项 | SQL 操 作 |
ALTER SEQUENCE | 任何ALTER SEQUENCE命令 |
ALTER TABLE | 任何ALTER TABLE命令 |
COMMENT TABLE | 添加注释到表、视图、物化视图或它们中的任何列 |
DELETE TABLE | 删除表或视图中的行 |
EXECUTE PROCEDURE | 执行程序包中的过程、函数或任何变量或游标 |
GRANT DIRECTORY | GRANT或REVOKE DIRECTORY对象表上的权限 |
GRANT PROCEDURE | GRANT或REVOKE过程、函数或程序包上的权限 |
GRANT SEQUENCE | GRANT或REVOKE序列上的权限 |
GRANT TABLE | GRANT或REVOKE表、视图或物化视图上的权限 |
GRANT TYPE | GRANT或REVOKE TYPE上的权限 |
INSERT TABLE | INSERT INTO表或视图 |
LOCK TABLE | 表或视图上的LOCK TABLE命令 |
SELECT SEQUENCE | 引用序列的CURRVAL或NEXTVAL的任何命令 |
SELECT TABLE | SELECT FROM表、视图或物化视图 |
UPDATE TABLE | 在表或视图上执行UPDATE |
一些示例可以帮助读者更清楚地了解所有这些选项。在示例数据库中,用户KSHELTON具有HR模式和其他模式中所有表上的权限。允许KSHELTON创建其中一些表上的索引,但如果有一些与执行计划改动相关的性能问题,则需要知道何时创建这些索引。可以使用如下命令审计KSHELTON创建的索引:
SQL> audit index by kshelton;
Audit succeeded.
后面的某一天,KSHELTON在HR.JOBS表上创建了一个索引:
SQL> create index job_title_idx on hr.jobs(job_title);
Index created.
检查数据字典视图DBA_AUDIT_TRAIL中的审计跟踪,可以看到KSHELTON实际上在8月12日的5:15 P.M.创建了索引:
SQL> select username, to_char(timestamp,'MM/DD/YY HH24:MI') Timestamp,
2 obj_name, action_name, sql_text from dba_audit_trail
3 where username = 'KSHELTON';
USERNAME TIMESTAMP OBJ_NAME ACTION_NAME SQL_TEXT
--------- -------------- -------------- -------------- ----------------
KSHELTON 08/12/07 17:15 JOB_TITLE_IDX CREATE INDEX create index hr.
job_title_idx on
hr.jobs(job_title)
1 row selected.
注意:
从Oracle Database 11g开始,只有在初始参数AUDIT_TRAIL被设置为DB_EXTENDED时,才填充DBA_AUDIT_TRAIL中的列SQL_TEXT和SQL_BIND。默认情况下,AUDIT_TRAIL的值是DB。
为了关闭HR.JOBS表上KSHELTONSCOTT的审计,可以使用noaudit命令,如下所示:
SQL> noaudit index by kshelton;
Noaudit succeeded.
SQL> noaudit index by scott;
也可能希望按常规方式审计成功的和不成功的注册登录,这需要两个audit命令:
SQL> audit session whenever successful;
Audit succeeded.
SQL> audit session whenever not successful;
Audit succeeded.
SQL> audit session whenever successful;
Audit succeeded.
SQL> audit session whenever not successful;
回顾审计跟踪,可以看到用户RJB在8月10日的失败的注册登录尝试:
SQL> select username, to_char(timestamp,'MM/DD/YY HH24:MI') Timestamp,
2 obj_name, returncode, action_name, sql_text from dba_audit_trail
3 where action_name in ('LOGON','LOGOFF')
4 and username in ('SCOTT','RJB','KSHELTON')
5 order by timestamp desc;
USERNAME TIMESTAMP OBJ_NAME RETURNCODE ACTION_NAME SQL_TEXT
--------- -------------- --------- --------- -------------- ----------
KSHELTON 08/12/07 17:04 0 LOGON
SCOTT 08/12/07 16:10 0 LOGOFF
RJB 08/12/07 11:35 0 LOGON
RJB 08/12/07 11:35 0 LOGON
RJB 08/11/07 22:51 0 LOGON
RJB 08/11/07 22:51 0 LOGOFF
RJB 08/11/07 21:55 0 LOGOFF
RJB 08/11/07 21:40 0 LOGOFF
RJB 08/10/07 22:52 0 LOGOFF
RJB 08/10/07 22:52 0 LOGOFF
RJB 08/10/07 22:52 1017 LOGON
RJB 08/10/07 12:23 0 LOGOFF
SCOTT 08/03/07 04:18 0 LOGOFF
13 rows selected.
SQL> select username, to_char(timestamp,'MM/DD/YY HH24:MI') Timestamp,
2 obj_name, returncode, action_name, sql_text from dba_audit_trail
3 where action_name in ('LOGON','LOGOFF')
4 order by timestamp desc;
USERNAME TIMESTAMP OBJ_NAME RETURNCODE ACTION_NAME SQL_TEXT
---------- -------------- ---------- ---------- ----------------- ----------
DBSNMP 02/24/04 21:55 0 LOGOFF
SYSMAN 02/24/04 21:54 0 LOGOFF
RJB 02/24/04 21:52 1017 LOGON
RJB 02/24/04 21:52 0 LOGON
DBSNMP 02/24/04 21:52 0 LOGOFF
SCOTT 02/24/04 21:52 0 LOGON
DBSNMP 02/24/04 21:51 0 LOGOFF
RJB 02/24/04 21:51 0 LOGOFF
SCOTT 02/24/04 21:38 0 LOGOFF
SCOTT 02/24/04 21:24 0 LOGOFF
RETURNCODE代表ORA错误消息。ORA-1017消息表明输入了不正确的密码。注意,如果仅对登录和退出感兴趣,可以改为使用DBA_AUDIT_SESSION视图。
语句审计也包括启动和关闭操作。虽然可以审计SYS.AUD$表中的命令shutdown immediate,但不可以审计SYS.AUD$中的startup命令,因为必须在可以将行添加到这个表中之前启动数据库。对于这些情况,可以在初始参数AUDIT_FILE_DEST中指定的目录$ORACLE_HOME/rdbms/aduit中查找,查看由系统管理员执行的启动操作的记录(默认情况下,此参数包含$ORACLE_HOME/admin/dw/adump)。下面是使用startup命令启动数据库时创建的文本文件:
Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 ¨C Productio
With the Partitioning, OLAP, Data Mining
and Real Application Testing options
ORACLE_HOME = /u01/app/oracle/product/11.1.0/db_1
System name: Linux
Node name: dw
Release: 2.6.9-55.0.2.0.1.EL
Version: #1 Mon Jun 25 14:24:38 PDT 2007
Machine: i686
Instance name: dw
Redo thread mounted by this instance: 1
Oracle process number: 44
Unix process pid: 28962, image: oracle@dw (TNS V1-V3)
Sun Aug 12 11:57:36 2007
ACTION : 'CONNECT'
DATABASE USER: '/'
PRIVILEGE : SYSDBA
CLIENT USER: oracle
CLIENT TERMINAL: pts/2
STATUS: 0
Audit file /u01/app/oracle/product/10.1.0/rdbms/audit/ora_2788.aud
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
ORACLE_HOME = /u01/app/oracle/product/10.1.0
System name: Linux
Node name: dw10g
Release: 2.4.21-9.0.1.EL
Version: #1 Mon Feb 9 22:26:52 EST 2004
Machine: i686
Instance name: dw
Redo thread mounted by this instance: 0
Oracle process number: 0
2788
Tue Feb 24 17:26:01 2004
ACTION : 'STARTUP'
DATABASE USER: '/'
PRIVILEGE : SYSDBA
CLIENT USER: oracle
CLIENT TERMINAL: Not Available
在该示例中,由主机系统上作为oracle角色连接的用户启动数据库,并且该用户使用操作系统验证连接到实例。下一节将介绍额外的系统管理员审计问题。
109.4.3 权限审计
审计系统权限具有与语句审计相同的基本语法,但审计系统权限是除了在sql_statement_clause(中,而不是在语句)中,指定系统权限。
例如,可能希望将ALTER TABLESPACE权限授予所有的DBA,但希望在发生这种情况时生成审计记录。启用对在这种权限的上启用审计的命令看起来类似于语句审计:
SQL> audit alter tablespace by access whenever successful;
Audit succeeded.
每次成功使用ALTER TABLESPACE权限时,都会将一行内容添加到SYS.AUD$。
使用SYSDBA和SYSOPER权限或者以SYS用户连接到数据库的系统管理员可以利用特殊的审计。为了启用这种额外的审计级别,可以设置初始参数AUDIT_SYS_OPERATIONS为TRUE。这种审计记录发送到与操作系统审计记录相同的位置。因此,这个位置是和操作系统相关的。所有在当使用其中一种权限时执行的所有SQL语句,以及作为用户SYS执行的任何SQL语句,都会发送到操作系统审计位置。
109.4.4 模式对象审计
审计对各种模式对象的访问看起来类似于语句审计和权限审计:
AUDIT schema_object_clause BY {SESSION | ACCESS}
WHENEVER [NOT] SUCCESSFUL;
AUDIT schema_object_clause BY {SESSION | ACCESS}
schema_object_clause指定对象访问的类型以及访问的对象。可以审计特定对象上1314种不同的操作类型,表109-19中列出了这些操作。
表109-19 对象审计选项
对 象 选 项 | 说 明 |
ALTER | 改变表、序列或物化视图 |
AUDIT | 审计任何对象上的命令 |
COMMENT | 添加注释到表、视图或物化视图 |
DELETE | 从表、视图或物化视图中删除行 |
EXECUTE | 执行过程、函数或程序包 |
FLASHBACK | 执行表或视图上的闪回操作 |
GRANT | 授予任何类型对象上的权限 |
INDEX | 创建表或物化视图上的索引 |
INSERT | 将行插入表、视图或物化视图中 |
LOCK | 锁定表、视图或物化视图 |
READ | 对执行DIRECTORY对象的内容上的执行读操作 |
RENAME | 重命名表、视图或过程 |
SELECT | 从表、视图、序列或物化视图中选择行 |
UPDATE | 更新表、视图或物化视图 |
如果希望审计HR.JOBS表上的所有insert和update命令,而不管谁正在进行更新,则每次该动作发生时,都可以使用如下所示的audit命令:
SQL> audit insert, update on hr.jobs by access whenever successful;
Audit successful.
SQL> audit insert, update on hr.jobs by access whenever successful;
用户KSHELTONTAMARA决定向添加两个新行到HR.JOBS表添加两个新行:
SQL> insert into hr.jobs (job_id, job_title, min_salary, max_salary)
2 values ('IN_CFO','Internet Chief Fun Officer', 7500, 50000);
1 row created.
SQL> insert into hr.jobs (job_id, job_title, min_salary, max_salary)
2 values ('OE_VLD','Order Entry CC Validation', 5500, 20000);
1 row created.
SQL> insert into hr.jobs (job_id, job_title, min_salary, max_salary)
2 values ('IN_CFO','Internet Chief Fun Officer', 7500, 50000);
1 row created.
SQL> insert into hr.jobs (job_id, job_title, min_salary, max_salary)
2 values ('OE_VLD','Order Entry CC Validation', 5500, 20000);
查看DBA_AUDIT_TRAIL视图,可以看到KSHELTONTAMARA会话中的两个insert命令:
USERNAME TIMESTAMP OWNER OBJ_NAME ACTION_NAME
SQL_TEXT
---------- -------------- -------- ---------- ---------------
---------------------------------------------------------------
KSHELTON 08/12/07 22:54 HR JOBS INSERT
insert into hr.jobs (job_id, job_title, min_salary, max_salary)
values ('IN_CFO','Internet Chief Fun Officer', 7500, 50000);
KSHELTON 08/12/07 22:53 HR JOBS INSERT
insert into hr.jobs (job_id, job_title, min_salary, max_salary)
values ('OE_VLD','Order Entry CC Validation', 5500, 20000);
KSHELTON 08/12/07 22:51 LOGON
3 rows selected.
USERNAME TIMESTAMP OWNER OBJ_NAME ACTION_NAME
SQL_TEXT
---------- -------------- -------- ---------- ---------------
---------------------------------------------------------------
TAMARA 02/24/04 22:54 HR JOBS INSERT
insert into hr.jobs (job_id, job_title, min_salary, max_salary)
values ('IN_CFO','Internet Chief Fun Officer', 7500, 50000);
TAMARA 02/24/04 22:53 HR JOBS INSERT
insert into hr.jobs (job_id, job_title, min_salary, max_salary)
values ('OE_VLD','Order Entry CC Validation', 5500, 20000);
TAMARA 02/24/04 22:51 LOGON
109.4.5 细粒度的审计
从Oracle9i开始,通过引入细粒度的对象审计,或称为FGA,审计变得更为关注某个方面,并且更为精确。由称为DBMS_FGA的PL/SQL程序包实现FGA。
使用标准的审计,可以轻松发现访问了哪些对象以及由谁访问,但无法知道访问了哪些行或列。细粒度的审计可解决这个问题,它不仅为需要访问的行指定谓词(或where子句),还指定了表中访问的列。通过只在访问某些行和列时审计对表的访问,可以极大地减少审计表条目的数量。
程序包DBMS_FGA具有4个过程:
ADD__POLICY | 添加使用谓词和审计列的审计策略 |
DROP_POLICY | 删除审计策略 |
DISABLE_POLICY | 禁用审计策略,但保留与表或视图关联的策略 |
ENABLE_POLICY | 启用策略 |
用户TAMARA通常每天访问HR.EMPLOYEES表,查找雇员的电子邮件地址。系统管理员怀疑TAMARA正在查看经理们的薪水信息,因此他们建立一个FGA策略,用于审计任何对经理对的SALARY列的任何访问:
begin
dbms_fga.add_policy(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'SAL_SELECT_AUDIT',
audit_condition => 'instr(job_id,''_MAN'') > 0',
audit_column => 'SALARY'
);
end;
begin
dbms_fga.add_policy(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'SAL_SELECT_AUDIT',
audit_condition => 'instr(job_id,''_MAN'') > 0',
audit_column => 'SALARY'
);
可以使用数据字典视图DBA_FGA_AUDIT_TRAIL访问细粒度审计的审计记录。如果一般需要查看标准的审计行和细粒度的审计行,则数据字典视图DBA_COMMON_AUDIT_TRAIL结合了这两种类型审计类型中的行。
继续看示例,用户TAMARA运行了如下两个SQL查询:
SQL> select employee_id, first_name, last_name, email from hr.employees
2 where employee_id = 114;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL
----------- ------------------ --------------------- --------------
114 Den Raphaely DRAPHEAL
1 row selected.
SQL> select employee_id, first_name, last_name, salary from hr.employees
2 where employee_id = 114;
EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY
----------- ------------------ ----------------------- ----------
114 Den Raphaely 11000
1 row selected.
SQL> select employee_id, first_name, last_name, email from hr.employees
2 where employee_id = 114;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL
----------- -------------------- ------------------------- --------------
114 Den Raphaely DRAPHEAL
1 row selected.
SQL> select employee_id, first_name, last_name, salary from hr.employees
2 where employee_id = 114;
EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY
----------- -------------------- ------------------------- ----------
114 Den Raphaely 11000
第一个查询访问经理信息,但没有访问SALARY列。第二个查询与第一个查询相同,但是访问了SALARY列,因此触发了FGA策略,从而在审计跟踪中生成了一行:
SQL> select to_char(timestamp,'mm/dd/yy hh24:mi') timestamp,
2 object_schema, object_name, policy_name, statement_type
3 from dba_fga_audit_trail
4 where db_user = 'TAMARA';
TIMESTAMP OBJECT_SCHEMA OBJECT_NAME POLICY_NAME STATEMENT_TYPE
-------------- ------------- ------------- ---------------- --------------
08/12/07 18:07 HR EMPLOYEES SAL_SELECT_AUDIT SELECT
1 row selected.
SQL> select to_char(timestamp,'mm/dd/yy hh24:mi') timestamp,
2 object_schema, object_name, policy_name, statement_type
3 from dba_fga_audit_trail
4 where db_user = 'TAMARA';
TIMESTAMP OBJECT_SCHEMA OBJECT_NAME POLICY_NAME STATEMENT_TYPE
-------------- -------------- -------------- ---------------- --------------
02/25/04 00:04 HR EMPLOYEES SAL_SELECT_AUDIT SELECT
因为在本章前面的VPD示例中建立了细粒度的访问控制来阻止放置对SALARY列的未授权访问,因此需要加倍检查复查策略函数,确保仍然正确限制约束了SALARY信息。细粒度的审计以及标准审计是确保首先正确建立授权策略的好方法。
109.4.6 与审计相关的数据字典视图
表109-20包含了与审计相关的数据字典视图。
表109-20 与审计相关的数据字典视图
数据字典视图 | 说 明 |
AUDIT_ACTIONS | 包含审计跟踪动作类型代码的描述,例如INSERT、DROP VIEW、DELETE、LOGON和LOCK |
DBA_AUDIT_OBJECT | 与数据库中对象相关的审计跟踪记录 |
DBA_AUDIT_POLICIES | 数据库中的细粒度审计策略 |
DBA_AUDIT_SESSION | 与CONNECT和DISCONNECT相关的所有审计跟踪记录 |
DBA_AUDIT_STATEMENT | 与GRANT、REVOKE、AUDIT、NOAUDIT和ALTER SYSTEM命令相关的审计跟踪条目 |
DBA_AUDIT_TRAIL | 包含标准审计跟踪条目。USER_AUDIT_TRAILUSER_TRAIL_AUDIT只包含已连接用户的审计行 |
DBA_FGA_AUDIT_TRAIL | 细粒度审计策略的审计跟踪条目 |
(续表)
数据字典视图 | 说 明 |
DBA_COMMON_AUDIT_TRAIL | 将标准的审计行和细粒度的审计行结合在一个视图中 |
DBA_OBJ_AUDIT_OPTS | 对数据库对象生效的审计选项 |
DBA_PRIV_AUDIT_OPTS | 对系统权限生效的审计选项 |
DBA_STMT_AUDIT_OPTS | 对语句生效的审计选项 |
109.4.7 保护审计跟踪
审计跟踪自身需要受到保护,特别是在非系统用户必须访问表SYS.AUD$时。内置的角色DELETE_ANY_CATALOG是非SYS用户可以访问审计跟踪的一种方法(例如,为了归档和截取审计跟踪,以确保它不会影响到SYS表空间中其他对象的空间需求)。
为了建立对审计跟踪自身上的审计,作为以SYSDBA身份连接到数据库,并运行下面的命令:
SQL> audit all on sys.aud$ by access;
Audit succeeded.
现在,所有针对表SYS.AUD$的动作,包括select、insert、update和delete,都记录在SYS.AUD$自身中。但是,您可能会问,如果某个人删除了标识对表SYS.AUD$访问的审计记录,这时会发生什么?此时将删除表中的行,但接着是然后插入另一行,记录行的删除。因此,总是存在一些针对SYS.AUD$表的(有意的或偶然的)活动的证据。此外,如果将AUDIT_SYS _OPERATIONS设置为True,使用as sysdba、as sysoper或以SYS自身连接的任何会话或者作为SYS自身的连接将记录到操作系统审计位置中,可能甚至Oracle DBA可能都无法访问该位置。因此,有许多合适适当的安全措施,用于确保记录数据库中所有权限的活动,以及隐藏该活动的任何尝试。
9.4.8 启用增强的审计
从Oracle Database 11g开始,数据库配置助手(Database Configuration Assistant,DBCA)很容易启用默认的(增强的)审计。虽然记录审计信息有一些系统开销,但兼容性需求(例如,Sarbanes-Oxley法案中规定的兼容性需求)要求严格监控所有业务操作,包括数据库中与安全相关的操作。
可以在创建数据库时或在数据库已经创建之后使用DBCA配置默认审计。如果已经改变了很多审计设置,并想要将审计选项重置为基线值,则在数据库已创建之后使用DBCA配置默认审计就非常有用。
除将初始参数AUDIT_TRAIL的值设置为DB外,默认审计设置还审计audit role命令本身。另外,在Audited Privileges选项卡的Oracle Enterprise Manager Audit Settings页面中,可以查看默认的审计权限。图9-9展示了默认的审计权限以及本章前面创建的其他两个审计权限。
图9-9 使用OEM显示审计权限
109.5 数据加密技术
数据加密可以增强数据库内部和外部的安全性。用户可能具有访问表中大多数列的合法需求,但如果对其中一列进行加密,并且用户不知道加密密钥,则无法使用相关的信息。同样的问题相同的概念也适用于需要通过网络安全发送的信息。本章已介绍的技术,包括验证、授权和审计,可以确保数据库用户合法访问数据,但不能阻止操作系统用户访问数据,因为这些用户可能具有操作系统文件的访问权,而数据库本身就是由这些操作系统文件组成的。
用户可以采用如下两种方法进行数据加密:一种方法是使用程序包DBMS_CRYPTO(在Oracle Database 10g中,这一程序包替换了Oracle9i中的DBMS_OBFUSCATION_TOOLKIT程序包);另一种方法是透明数据加密,这种方法以全局方式存储加密密钥,并包含加密整个表空间的方法。
9.5.1 DBMS_CRYPTO程序包
作为Oracle 10g的新增内容,程序包DBMS_CRYPTO代替了DBMS_OBFUSCATION_ TOOLKIT,并且包括Advanced Encryption Standard(AES)加密算法,(AES算法它代替了Data Encryption Standard(DES)算法)。
DBMS_CRYPTO中的过程可以生成私有密钥,也可以自己指定并存储密钥。与只可以加密RAW或VARCHAR2数据类型的DBMS_OBFUSCATION_TOOLKIT不同的是,DBMS_ CRYPTO可以加密BLOB和CLOB类型。
9.5.2 透明数据加密
透明数据加密是一种基于密钥的访问控制系统,它依赖于外部模块实施授权。包含加密列的每个表都有自己的加密密钥,加密密钥又由为数据库创建的主密钥来加密,加密密钥以加密方式存储在数据库中,但主密钥并不存储在数据库中。重点要强调的是“透明”这一术语——当访问表中或加密表空间中的加密列时,授权用户不必指定密码或密钥。
● 虽然Oracle Database 11g大大地增强了透明数据加密特性,但它的使用仍然受到一些限制,例如,不能使用外键约束对列进行加密,因为每个表都有一个唯一的列加密密钥。一般来说,这不应该是什么问题,因为外键约束中使用的密钥应该是系统生成的、唯一的和非智能的。表的业务键和其他业务属性可能更需要加密,且它们通常并不参与与其他表的外键关系。其他数据库特性和类型也不适合进行透明数据加密:
● 除B-树索引外的其他索引类型
● 索引的范围扫描搜索
● BFILE(外部对象)
● 物化视图日志
● 同步的更改数据捕获(Synchronous Change Data Capture)
● 可移植的表空间
● 原来的导入/导出实用工具(Oracle 9i及更早版本)
另外还可以选择使用DBMS_CRYPTO以手动方式加密这些类型和特性。
注意:
在Oracle Database 11g中,现在可以加密内部大对象,例如BLOB和CLOB类型。
1. 创建Oracle钱夹
使用Oracle企业管理器可以为透明数据加密创建一个钱夹。选择Server选项卡,然后单击Security Heading下面的Transparent Data Encryption链接,将会看到如图9-10所示的页面。在此例中,还没有创建钱夹。文件sqlnet.ora用ENCRYPTION_WALLET_LOCATION变量存储钱夹的位置。如果sqlnet.ora文件中不存在此变量,则将钱夹创建在$ORACLE_HOME/admin/ database_name/wallet中,在此例中是/u01/app/oracle/admin/dw/wallet。
图9-10 透明数据加密:创建一个钱夹
要创建加密密钥,并将它放在钱夹中,需要创建一个钱夹密码,此密码至少有10个字符,要包含大写字母、小写字母、数字和标点符号。单击OK,创建钱夹,可以看到如图9-11所示的页面。
图9-11 透明数据加密:钱夹处于打开状态
如果主密钥被泄密,则可以使用图9-10中的页面重新创建主密钥。也可以关闭钱夹——禁用透明数据加密(Transparent Data Encryption)——并阻止访问任何加密的表列或表空间。
创建、打开和关闭钱夹的等价SQL命令非常简单,且输入这些命令所花费的时间可能比使用Oracle企业管理器要少!要创建新的密钥,并在钱夹还不存在时创建钱夹,则使用alter system命令,如下所示:
SQL> alter system set encryption key identified by "Uni123#Lng";
System altered.
SQL>
注意将钱夹密钥放在双引号中,这一点非常重要。如果不将钱夹密钥用双引号引起来,则密码将映射所有小写字母,钱夹将不处于打开状态。数据库实例被停机并重新启动之后,如果此任务未以其他方式自动化,则需要使用alter system命令打开钱夹:
SQL> alter system set encryption wallet open identified by "Uni123#Lng";
System altered.
SQL>
最后,通过关闭钱夹,随时可以毫不费力地禁用对数据库中所有加密列的访问:
SQL> alter system set encryption wallet close;
System altered.
SQL>
要频繁地备份钱夹,且不要忘记钱夹密钥(或者是安全管理员不应该忘记钱夹密钥,安全管理员可以是与DBA不同的角色),因为丢失钱夹或钱夹密码将无法对任何加密的列或表空间进行解密。
2. 加密表
可以加密一个或多个表的一列或多列,具体做法是,在create table命令中的列的数据类型后面添加encrypt关键字;或者是在已存在列的列名后面添加encrypt关键字。例如,要加密EMPLOYEES表的SALARY列,则使用如下命令:
SQL> alter table employees modify (salary encrypt);
Table altered.
SQL>
以前有权访问SALARY列的任何用户仍具有对此列的相同访问权,对用户来说,这完全是透明的。唯一的区别在于,访问包含EMPLOYEES表的操作系统文件的任何人都无法破译SALARY列。
3. 加密表空间
要加密整个数据库,则必须将COMPATIBLE初始化参数设置为11.1.0.0.0,这是Oracle Database 11g的默认设置。如果数据库已从以前的版本升级,且将COMPATIBLE参数改变为11.1.0.0.0,则这种改变是不可逆的。
已存在的表空间不能加密,要加密已有表空间的内容,则必须用ENCRYPTION选项创建一个新的表空间,并将已有的对象复制或移动到新的表空间。Oracle企业管理器可以很容易地创建一个新的加密表空间。在图9-12中,创建一个新的名为USERS_CRYPT的表空间,其大小是500MB,位于ASM磁盘组中。
图9-12 创建加密的表空间
单击Encryption Options按钮,可以看到以前创建的钱夹的状态(钱夹必须处于打开状态才能创建加密的表空间),可以为表空间选择想要使用的加密算法。单击Continue按钮之后,如图9-13所示,返回到Create Tablespace页面。
单击Show SQL按钮,可以看到Oracle企业管理器创建表空间将使用的SQL命令:
CREATE SMALLFILE TABLESPACE "USERS_CRYPT"
DATAFILE'+DATA' SIZE 500M LOGGING EXTENT MANAGEMENT LOCAL
SEGMENT SPACE MANAGEMENT AUTO NOCOMPRESS ENCRYPTION
USING 'AES256' DEFAULT STORAGE(ENCRYPT)
单击Return按钮,然后单击OK按钮,则Oracle企业管理器创建表空间。
图9-13 指定加密的表空间选项
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/673037/viewspace-592276/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/673037/viewspace-592276/