作者:Arup Nanda
了解如何使用 Oracle 数据库的细粒度审计特性来跟踪对表中特定行的只读访问 ― 以及更多信息
传统的 Oracle 数据库审计选件允许您在宏观级别上跟踪用户在对象上所执行的操作 ― 例如,如果您审计对某个表的SELECT语句,则可以跟踪是谁从表中选择了数据。但是,您不知道他们选择了什么。利用数据操纵语句 ― 如INSERT、UPDATE或DELETE― 您可以通过使用触发器或使用 Oracle LogMiner 实用程序来分析归档日志,从而捕获任何的更改。因为简单的SELECT语句是不操纵数据的,它们既不启动触发器,也不记入到那些以后可以进行挖掘的归档日志中,所以这两种技术在涉及到SELECT语句的地方无法满足要求。
Oracle9i Database 推出了一种称为细粒度审计 (FGA) 的新特性,它改变了这种局面。该特性允许您将单个的 SELECT语句联同用户提交的确切语句一起进行审计。除了简单地跟踪语句之外,FGA 还通过在每次用户选择特定的数据集时执行一段代码,提供了一种方法来模拟用于SELECT语句的触发器。在分为三部分的这一系列文章中,我将说明如何使用 FGA 解决实际问题。这第一部分的主要内容是构建基本的 FGA 系统。
示例安装
我们的示例基于一个银行系统,已经通过应用程序级的审计按照传统提供了用户访问特定数据的审计线索。但是,只要用户使用诸如 SQL*Plus 等工具从应用程序以外的地方访问数据,该系统就不能满足要求。在本文中,我将说明作为 DBA 的您能够如何使用 FGA 来完成捕获用户对特定行的SELECT访问的任务,无论访问的工具或机制是什么。
在我们的示例中,数据库有一个名为 ACCOUNTS 的表,由模式 BANK 拥有,其结构如下:
Name Null? Type ---------------- -------- ------------ ACCT_NO NOT NULL NUMBER CUST_ID NOT NULL NUMBER BALANCE NUMBER(15,2)
为了构造一个能够对任何在此表中选择的用户进行审计的系统,您需要定义对该表的FGA 策略如下:
begin dbms_fga.add_policy ( object_schema=>'BANK', object_name=>'ACCOUNTS', policy_name=>'ACCOUNTS_ACCESS' ); end;
这段代码必须由具有执行程序包dbms_fga 权限的用户来执行。 但是,为了提高安全性,建议不要对用户 BANK(将要被审计的表的所有者)授予执行权限;而应该将权限授予一个安全的用户(比如 SECMAN),此用户应该执行添加策略的过程。
在定义了策略以后,当用户以通常的方式对表进行查询时,如下所示:
select * from bank.accounts;
审计线索记录此操作。您可以使用以下语句查看线索:
select timestamp, db_user, os_user, object_schema, object_name, sql_text from dba_fga_audit_trail; TIMESTAMP DB_USER OS_USER OBJECT_ OBJECT_N SQL_TEXT --------- ------- ------- ------- -------- ---------------------- 22-SEP-03 BANK ananda BANK ACCOUNTS select * from accounts
注意名为DBA_FGA_AUDIT_TRAIL的新视图,它记录细粒度的访问信息。其中显示了审计事件的时间标记、查询者的数据库用户 ID、操作系统用户 ID、查询中所使用表的名称和所有者,最后还有确切的查询语句。在 Oracle9i Database 之前不可能得到这种信息,但随着 FGA 的推出,获得此信息变得轻而易举。
在 Oracle9i Database 中,FGA 只能捕获 SELECT语句。利用 Oracle Database 10g,FGA 还可以处理 DML 语句 ― INSERT、UPDATE和DELETE― 使其成为完整的审计特性。在本系列的第 3 部分,我将详细说明这些新的功能。
审计列和审计条件
让我们更详细地检查前面的示例。我们要求审计对该表所使用的任何SELECT语句。但是在现实中,可能不必要这样做,并且这样可能会使存储线索的审计表承受不起。当用户选择含有敏感信息的余额列时,银行可能需要进行审计,但当用户选择特定客户的帐号时,可能不需要进行审计。列 BALANCE(选择它可触发审计)称为审计列,在此情况下,dbms_fga.add_policy过程的参数指定该列如下:
audit_column => 'BALANCE'
如果每次用户从表中选择时都记录审计线索,则线索的大小将增长,导致空间和管理问题,因此您可能希望只有在满足特定条件时进行审计,而不是每次都进行审计。也许只有当用户访问极为富有的户主帐号时,银行需要审计 ― 例如,只有当用户选择了余额为 11,000 美元或更多的帐号时需要审计。这种类型的条件称为审计条件,并作为一项参数传递到dbms_fga.add_policy过程,如下所示:
audit_condition => 'BALANCE >= 11000'
让我们来看这两个参数如何起作用。现在策略定义的形式类似于:
begin dbms_fga.add_policy ( object_schema=>'BANK', object_name=>'ACCOUNTS', policy_name=>'ACCOUNTS_ACCESS', audit_column => 'BALANCE', audit_condition => 'BALANCE >= 11000' ); end;
在此情况下,只有当用户选择列 BALANCE 并且检索的行包含大于或等于 $11,000 的余额时,才会审计该操作。如果这两个条件中有一个不为真,则该操作不会被写入到审计线索中。 表 1 中的示例演示了何时审计操作和何时不审计操作的各种情况。
优化器模式
FGA 需要基于开销的优化 (CBO),以便正确地工作。在基于规则的优化时,只要用户从表中进行选择,无论是否选择了相关的列,都始终生成审计线索,增加了误导项目出现的可能性。为使 FGA 正确地工作,除了在实例级启用 CBO 之外,在 SQL 语句中应该没有规则暗示,并且必须至少使用评估选项对查询中的所有表进行分析。
管理 FGA 策略
在前文中您看到了如何添加 FGA 策略。要删除策略,您可以使用以下语句:
begin dbms_fga.drop_policy ( object_schema => 'BANK', object_name => 'ACCOUNTS', policy_name => 'ACCOUNTS_ACCESS' ); end;
对于更改策略而言,没有随取随用的解决方案。要更改策略中的任何参数,必须删除策略,再使用更改后的参数添加策略。
有时您可能需要临时禁用审计收集 ― 例如,如果您希望将线索表移动到不同的表空间或者要删除线索表。您可以按如下方法禁用 FGA 策略:
begin dbms_fga.enable_policy ( object_schema => 'BANK', object_name => 'ACCOUNTS', policy_name => 'ACCOUNTS_ACCESS', enable => FALSE ); end;
要重新启用它,可使用同一函数,但是将参数enable设置为TRUE。
处理器模块
FGA 的功能不只是记录审计线索中的事件;FGA 还可以任意执行过程。过程可以执行一项操作,比如当用户从表中选择特定行时向审计者发送电子邮件警告,或者可以写到不同的审计线索中。这种存储代码段可以是独立的过程或者是程序包中的过程,称为策略的处理器模块。实际上由于安全性原因,它不必与基表本身处于同一模式中,您可能希望特意将它放置在不同的模式中。由于只要SELECT出现时过程就会执行,非常类似于 DML 语句启动的触发器,您还可以将其看作SELECT语句触发器。以下参数指定将一个处理器模块指定给策略:
handler_schema拥有数据过程的模式
handler_module过程名称
处理器模块还可以采用程序包的名称来代替过程名称。在这种情况下,参数handler_module在package.procedure的格式中指定。
FGA 数据字典视图
FGA 策略的定义位于数据字典视图 DBA_AUDIT_POLICIES 中。 表 2 包含该视图中一些重要列的简短描述。
审计线索收集在 SYS 拥有的表 FGA_LOG$ 中。对于 SYS 拥有的任何原始表,此表上的某些视图以对用户友好的方式显示信息。DBA_FGA_AUDIT_TRAIL 是该表上的一个视图。 表 3 包含该视图中重要列的简短描述。
一个重要的列是 SQL_BIND,它指定查询中使用的绑定变量的值 ― 这是显著增强该工具功能的一项信息。
另一个重要的列是 SCN,当发生特定的查询时,它记录系统更改号。此信息用于识别用户在特定时间看到了什么,而不是现在的值,它使用了闪回查询,这种查询能够显示在指定的 SCN 值时的数据。我将在本系列的第 2 部分中详细说明这种功能强大的特性。
视图和 FGA
到目前为止我已经讨论了在表上应用 FGA;现在让我们来看如何在视图上使用 FGA。假定在 ACCOUNTS 表上定义视图 VW_ACCOUNTS 如下:
create view vw_accounts as select * from accounts;
现在,如果用户从视图中而不是从表中进行选择:
select * from vw_accounts;
您将看到以下审计线索:
select object_name, sql_text from dba_fga_audit_trail; OBJECT_NAME SQL_TEXT ----------- ------------------------------------------------- ACCOUNTS select * from vw_accounts
注意,是基表名称而不是视图名称出现在 OBJECT_NAME 列中,因为视图中的选择是从基表中进行选择。但是,SQL_TEXT 列记录了用户提交的实际语句,而这正是您希望了解的。
接下来的步骤
阅读有关 DBMS_FGA 程序包的更多信息
访问 Oracle 数据库主页
访问 Oracle 平台安全性主页
如果您只希望审计对视图的查询而不是对表的查询,可以对视图本身建立策略。通过将视图名称而不是表的名称传递给打包的过程dbms_fga.add_policy中的参数object_name,可以完成这项工作。随后 DBA_FGA_AUDIT_TRAIL 中的 OBJECT_NAME 列将显示视图的名称,并且不会出现有关表访问的附加记录。
其它用途
除了记录对表的选择访问,FGA 还可用于某些其它情况:
结论
FGA 使您在 Oracle 数据库中支持隐私和职能策略。因为审计发生在数据库内部而不是应用程序中,所以无论用户使用的访问方法是什么(通过诸如 SQL*Plus 等工具或者应用程序),都对操作进行审计,允许进行非常简单的设置。
下一次我将讨论高级 FGA 技术以及 Oracle Database 10g 中的新特性,这些特性使 FGA 的功能极为强大,适用于所有类型的审计情况。
Arup Nanda ([email protected] ) 是 IntelliClaim 的首席数据库设计人员,该公司位于 Connecticut 的 Norwalk,提供对卫生保健保险索赔管理高度安全和基于规则的优化。他是 2003 年度 Oracle DBA 奖的获得者,并与他人合作编著了即将出版的Oracle 隐私安全性审计(Rampant TechPress 出版,2003)。
表 1:演示何时审计操作以及何时不审计操作的各种情况
SQL 语句
审计状态
select balance from accounts;
进行审计。用户选择了在添加策略时所指定的审计列 BALANCE。
select * from accounts;
进行审计。即使用户没有明确指定列 BALANCE,* 也隐含地选择了它。
select cust_id from accounts where balance < 10000;
进行审计。即使用户没有明确指定列 BALANCE,where 子句也隐含地选择了它。
select cust_id from accounts;
不进行审计。用户没有选择列 BALANCE。
select count(*) from accounts;
不进行审计。用户没有明确或隐含地选择列 BALANCE。
表 2:数据字典视图 DBA_AUDIT_POLICIES 中重要的列
OBJECT_SCHEMA
对其定义了 FGA 策略的表或视图的所有者
OBJECT_NAME
表或视图的名称
POLICY_NAME
策略的名称 ― 例如,ACCOUNTS_ACCESS
POLICY_TEXT
在添加策略时指定的审计条件 ― 例如,BALANCE >= 11000
POLICY_COLUMN
审计列 ― 例如,BALANCE
ENABLED
如果启用则为 YES,否则为 NO
PF_SCHEMA
拥有策略处理器模块的模式(如果存在)
PF_PACKAGE
处理器模块的程序包名称(如果存在)
PF_FUNCTION
处理器模块的过程名称(如果存在)
表 3:DBA_FGA_AUDIT_TRAIL 视图中重要的列
SESSION_ID
审计会话标识符;与 V$SESSION 视图中的会话标识符不同
TIMESTAMP
审计记录生成时的时间标记
DB_USER
发出查询的数据库用户
OS_USER
操作系统用户
USERHOST
用户连接的机器的主机名
CLIENT_ID
客户标识符(如果由对打包过程 dbms_session.set_identifier 的调用所设置)
EXT_NAME
外部认证的客户名称,如 LDAP 用户
OBJECT_SCHEMA
对该表的访问触发了审计的表所有者
OBJECT_NAME
对该表的SELECT操作触发了审计的表名称
POLICY_NAME
触发审计的策略名称(如果对表定义了多个策略,则每个策略将插入一条记录。在此情况下,该列显示哪些行是由哪个策略插入的。)
SCN
记录了审计的 Oracle 系统更改号
SQL_TEXT
由用户提交的 SQL 语句
SQL_BIND
由 SQL 语句使用的绑定变量(如果存在)
在掌握了基本概念之后,了解细粒度审计中的高级概念,并解决应用程序用户模型中的问题。
在本系列的第 1 部分 中,我说明了如何构建一个细粒度审计 (FGA) 系统以审计选择语句,并根据特定的条件捕获语句。在常规的审计中或通过使用 DML 触发器和 Log Miner 工具,上述工作几乎不可能完成。除了创建审计跟踪,我们还了解了当满足审计条件时,如何让审计跟踪执行一个用户定义的过程。
在这一部分内容中,我们将探讨高级的 FGA 概念,如应用程序用户模型和重建用户查看的数据。
应用程序用户问题
上篇文章解释了如何构建一个 FGA 系统,从而在表 FGA_LOG$ 中记录发出查询的数据库用户。在数据库用户映射到一个物理用户的情况下,这一方法非常有效。但是,在有些情况下(如在基于 web 的应用程序中),应用服务器使用固定的用户 id(如 APPUSER)连接到数据库。应用程序用户由应用程序验证。例如,用户 SCOTT 连接到应用程序,应用程序验证用户,然后使用用户 id APPUSER 连接到数据库。如果在此环境中使用了 FGA,FGA 将显示用户为 APPUSER 而不是 SCOTT。同样,另一个选择数据的应用程序用户 JANE 也将被记录为 APPUSER 而不是 JANE。由于记录的用户名不是实际的用户,审计没有发挥作用。
立刻迸入脑海里的解决方案是在数据库中创建应用程序用户,让应用程序使用用户 id 和口令连接到数据库。例如,在上述示例中,用户 SCOTT 和 JANE 将是数据库中的用户,无需存在一个名为 APPUSER 的用户。这一方法既简单又安全,因为数据库验证用户,并且不管连接采用何种方式,用户始终保持不变。
但是在该方法下,所有的用户都必须在数据库中创建,这将导致管理极其困难―尤其当用户数量达到几千人(这在 web 应用程序中很常见)。此外,用户必须记住他们在不同地方的用户名和口令,这一麻烦正是一次性登录存在的全部理由。尽管使用 Oracle Advanced Security Option(ASO,也称为高级联网选项或者 ANO)以及 Oracle Internet Directory 可以解决这一问题,但是对于大多数网站,此解决方案不可行。
在 web 应用程序中,连接池的概念还需要使用一个普通的用户 id。如图 1 所示,应用服务器使用用户 APPUSER 创建了几个与数据库的连接。当应用程序用户(如 SCOTT)需要涉及数据库的服务时,应用程序使用与数据库的一个连接来完成请求。这一模型可以使用几个连接支持许多用户,因此受到 web 应用程序设计师的欢迎。但需要重申的是,这一方法不会在 FGA 中记录正确的用户名。
使用应用程序用户 id 的理由非常充分。因此,理想的解决方案是在 FGA 框架中使用应用程序用户 id,而不是绕过该需求。
客户端标识符
Oracle9i 引进了 客户端标识符 (一个可以作为会话属性的值)的概念。任何值都可以放入该属性中,在我们的情况中,可以使用它来代表 实际的 用户名,如 JANE。尽管数据库用户名被记录为 APPUSER 或其它名字,客户端标识符可以存放值 JANE。
可以将会话的客户端标识符设置为某个值,方法是调用 Oracle 提供的 APIdbms_session.set_identifier 。下列语句设置了会话中的标识符 SCOTT:
EXECUTE DBMS_SESSION.SET_IDENTIFIER ('SCOTT')
设置完以后,此标识符将在该会话的 V$SESSION 视图中显示。
SELECT CLIENT_IDENTIFIER FROM V$SESSION WHERE AUDSID = USERENV('SESSIONID');
显示的值为 SCOTT,正如前面所设。那么,该值的重要性体现在哪里?除了出现在 V$SESSION 视图中,此信息还会显示在 FGA 表 FGA_LOG$ 以及随后的 DBA_FGA_AUDIT_TRAIL 视图的 CLIENT_ID 列中,如下所示:
SELECT DB_USER, CLIENT_ID FROM DBA_FGA_AUDIT_TRAIL;
尽管 DB_USER 列显示了 数据库 用户,真正的 应用程序 用户(如果放在标识符中)可以在 FGA 跟踪中捕获。当审计应用程序用户时,这是一个需要理解和应用的非常重要的概念。
客户端标识符还出现在常规的审计跟踪中,而不仅仅出现在细粒度的审计跟踪中。因此,在各种审计情况下(从常规类型到高级的细粒度类型),这个值都提供缺少的链接来表示实际的用户。在常规的审计跟踪中,表 AUD$ 在列 CLIENTID 中记录了客户端标识符(如果在会话中已设置)。该值显示在相关的视图中,如列 CLIENT_ID 中的 DBA_AUDIT_TRAIL。
令人感到困难的是如何正确地设置这一值。请记住,在一个启用 web 且具有连接池的应用程序中,数据库会话服务于许多用户会话―在会话之初仅设置一次值没有作用。此外,应用程序必须在每次调用数据库之前设置客户端标识符,从而 FGA 能够看到真正的应用程序用户 id。当另一个应用程序线程重用数据库会话时,标识符被设置为另一个用户 id,标识 FGA 跟踪中的该名用户。
另一种方法是设置一个 cookie,并使用该值设置客户端标识符。这种方法更安全,因为 cookie 可以包括其它的验证信息,而通过常规的会话根本不可能设置这些信息。然后,可以正确地解码标识符中的值,获得真正的用户名;但是验证信息为该值添加了一个数据完整性组件。
应用程序环境
使用客户端标识符有它的优点,但也存在严重的安全威胁:这种设置假定用户将值设为真正的用户 id,但这一点无法得到保证。恶意攻击的用户可以连接然后将该值设为不同的用户 id,严重地破坏审计跟踪的真实性。在 web 应用程序中,使用 cookie 存储客户端标识符使得破坏更困难(如果不是不可能);但是在普通的应用程序中,仅仅使用客户端标识符,安全性可能不尽人意。我们需要一种更安全的方法来捕获审计跟踪中的应用程序用户。
进入解决方案: 应用程序环境 .应用程序环境类似于会话变量;一旦设置了,任何时候都可以在会话中访问它们。可以在另一个会话中设置一个不同的值,而在整个会话中都看不到这个值。环境具有的属性类似于表的列;但与表不同的是,环境不是片段对象,属性可以在运行时而不是设计时定义。
可以使用下列 SQL 创建应用程序环境:
create context my_app_ctx using set_my_app_ctx;
注意,子句 using set_my_app_ctx 意味着环境中的属性 只能 通过名为 set_my_app_ctx 的过程来操作,该过程定义如下:
create or replace procedure set_my_app_ctx ( p_app_user in varchar2 := USER ) is begin dbms_session.set_context('MY_APP_CTX','APP_USERID', p_app_user); end;
此过程通过调用 dbms_session.set_context API,简单地将属性 APP_USERID 设置为输入参数传递的值。因此,如果用户直接调用此 API,其结果会如何呢?
SQL> exec dbms_session.set_context('MY_APP_CTX','APP_USERID', 'JUNE') BEGIN dbms_session.set_context('MY_APP_CTX','APP_USERID', 'JUNE'); END; * ERROR at line 1: ORA-01031: insufficient privileges ORA-06512: at "SYS.DBMS_SESSION", line 78 ORA-06512: at line 1
注意错误 ORA-01031:insufficient privileges 有点令人误解。用户的确对 SYS.DBMS_SESSION 有执行权限,但是通过调用它来设置环境属性是违法的,因此出现了错误。但是,当用户调用受信任的过程来设置环境属性:
SQL> execute set_my_app_ctx ('AAAA') PL/SQL procedure successfully completed.
设置成功了。因为环境属性只能通过它的过程(正确叫法是 受信任的 过程)来设置。这是应用程序环境一个非常重要的属性,将在 FGA 中得到使用。
一旦设置了环境属性,可以通过调用函数 SYS_CONTEXT 来检索它。在上述代码中设置完环境后,可以通过下列语句来查看环境:
select sys_context('MY_APP_CTX','APP_USERID') from dual;
该语句返回属性值。如果可以通过安全的方式设置环境,则可以利用环境来设置客户端标识符。
基于我们现有的知识,以下是可能的解决方案:
这一方法可以使用用户定义的审计跟踪得到进一步的完善。其中客户端标识符可以被设置为从应用程序环境中检索到的值。我们将在本系列的第三部分(以及最后一部分)中讨论该方法。
用户看到的状况如何呢?
细粒度的审计跟踪记录用户连同绑定变量的值(如果存在)一同输入的实际语句。下面是从跟踪中可以捕获的一条典型语句:
select balance from accounts where acct_no = 10034;
假设这一事务处理发生在 10:00AM,而现在是 11:00AM。在这种情况下,帐户余额可能已经更新了。如果审计员(或者 DBA)决定查看用户在 那一时刻 看到的实际情况,列的当前值可能没有反映准确的信息。在工业间谍的情况中,或者要建立动机或模式,用户非常有必要看到确切的数据。即使 FGA 没有捕获该时刻的准确数据,我们也可以使用跟踪中捕获的另一条信息看到该数据。
在本系列的第 1 部分 中,我们看到了视图 DBA_FGA_AUDIT_TRAIL 的结构。该视图记录了与会话和用户相关的几条关键信息。此处的相关列是 SCN,它记录了生成跟踪时的系统更改号。使用闪回查询,我们可以重建该点时刻的数据。假设审计跟踪中 SCN 的值为 123456,我们可以如下所示进行查询:
select balance from accounts as of SCN 123456 where acct_no = 10034;
SCN 123456 的子句将从表中返回该点时刻而不是现在的余额,这正是我们所需要的。
由于闪回局限于撤消保留时段,在该时段外发生的事务会丢失。但是,审计员可能寻找自事件发生 后的跟踪,因此,使用闪回仅捕获重要的列,执行一次定期的审计收集是比较谨慎的做法。
现在您已经掌握了各种环境中的 FGA,下面您将了解到它如何能够在 Oracle Database 10g 中起到更大的作用
在本系列的前两个部分中,我向您介绍了细粒度审计 (FGA) 的概念,它用来在 Oracle9i Database 和更高版本中跟踪选定的语句。我还说明了如何在复杂的环境中(比如说在一个 Web 应用程序内部)通过应用程序上下文和客户端标识符来使用这个特性。这两篇文章为您提供了足够的信息,使您可以为几乎所有类型的数据库系统(无论它有多复杂)构建一个 FGA 设置程序。在这个第三部分,同时也是最后一部分中,我将说明 Oracle Database 10g 为表带来的 FGA 增强。
Oracle9i Database 中的 FGA
让我们简要地重述一下 FGA 的好处。关于完整的讨论,请参考本系列的第 1 部分和第 2 部分 。
正规审计(通过 AUDIT 语句)记录使用的语句 ― 如 SELECT 或 INSERT ― 以及谁发出它、从哪一个终端、什么时候等等。然而,信息最重要的部分 ― 哪条 特定 记录被修改了,以及数据本身的变化 ― 没有捕获到。相反,许多用户编写触发器来捕获变化前后的数据值,并把它们记录在用户自定义的表中。但因为触发器只可能在 DML 语句(如 insert、update 和 delete)上使用,所以访问的一个主要的方面 ― SELECT 语句 ― 不能通过这种途径来审计。
因此 FGA 的价值在于:它只捕获 SELECT 语句。与触发器和 Log Miner 工具一起,FGA 提供了一种机制来审计各种类型的值修改和不涉及到修改的数据访问。
FGA 不仅填补了审计 SELECT 语句的空白,而且还提供了其它的一些引人注目的额外好处,这些好处使得数据库管理员的工作变得更加轻松。例如,FGA 允许一个用户自定义的过程在指定的审计条件出现的时候执行,因此您可以认为它是 SELECT 语句上的一个触发器 ― 这是一个在其它方式下没有提供的功能。这个技巧可能非常有用 ― 例如,任何时候当有人选择了收入超过 1 百万美元的员工的工资记录时,发送一封邮件给一个安全审计员,以在用户自定义的表中生成审计记录,这些表可以不受限制地进行处理(这与 SYS 拥有的 FGA_LOG$ 表不同);利用审计线索识别数据访问类型,以找出最可能的索引机制等等。
由于这些及其它的原因,FGA 成为数据库管理员工具箱中最重要的工具之一。不过,在 Oracle9i Database 中,它缺少一个重要的特性:在 SELECT 语句之外的语句上使用。
所有类型的 DML
在 Oracle Database 10g 中,FGA 已变得很完善 ― 它可以审计所有类型的 DML 语句,而不只是 SELECT。
让我们看一个例子。在本系列的第 1 部分中,我介绍了一个名称为 ACCOUNTS 的表。在那个表上定义的 FGA 策略为:
begin dbms_fga.add_policy ( object_schema => 'BANK', object_name => 'ACCOUNTS', policy_name => 'ACCOUNTS_ACCESS', audit_column => 'BALANCE', audit_condition => 'BALANCE >= 11000' ); end;
在 Oracle 9i Database 下,这个策略只能审计 SELECT 语句。然而,在 Oracle Database 10g 中,您可以扩展它,使它包含 INSERT、UPDATE 和 DELETE。您可以通过指定一个新的参数来实现这个目的:
statement_types => 'INSERT, UPDATE, DELETE, SELECT'
这个参数将在所有包括的语句类型上启用审计。您甚至可能考虑为每种语句类型创建单独的策略,这将允许您随意地启用和禁用策略 ― 尤其是,控制审计线索的创建,以管理它们占用的空间。不过, statement_type 参数默认情况下只审计 SELECT 语句。
在策略中添加新的参数之后,发出以下语句:
update accounts set balance = 1200 where balance >= 3000;
这将使一条记录插入到 FGA_LOG$ 表中。注意这条记录是由一个自动事务插入的;即使您回滚 update 语句,这条记录也将存在。您可以从另一个会话来检查线索是否存在。
select lsqltext from fga_log$; LSQLTEXT -------------------------------------------------------- update accounts set balance = 3100 where balance >= 3000
该行还包括其它所有的相关详细信息(如表名称、策略名称和事务 ID)。
与触发器方法相比较
那么 FGA 能做什么原来的基于触发器的方法不能做的事情?
在 Oracle Database 10g 之前,DML 语句审计是在一个触发器中进行的,类似于以下方式(注意:这不是真实的代码,只是一个代表性的示例):
CREATE TRIGGER XXXXX ON
Table AFTER INSERT OR UPDATE OR DELETE FOR EACH ROW BEGIN INSERT INTO AUDIT_LOG
Old Value, New Value, Time ..... END
触发器捕获旧的值和新的值,并填充 AUDIT_LOG 表。如果需要,还可以将它变为自动事务。最大的问题是触发器是对每行触发的,而不是每条语句一次。例如,以下语句:
update accounts set balance = 1200 where balance >= 3000;
对全部 10,000 条记录触发,在审计表中插入 10,000 行。这种方法可能严重地损害 update 语句的性能,甚至可能因审计线索中的空间问题而导致失败。使用语句触发器也无济于事,因为它不能捕获个别记录的任何新的或旧的值。比较而言,在 FGA 方法中,只创建一条记录,并且插入只在每条语句上执行一次,而不是每行一次 ― 即使有的话,对性能的影响也很小。
在 FGA 中,您可以指定相关的列来限定审计线索仅在这些列被访问时才创建。在触发器中,通过使用触发器定义的 WHERE,这种功能也可以实现。不过,其中存在一个非常重要的差异 ― 在触发器中,只有 当列被修改 时(而不是被访问时)才检查它们。在 FGA 中,无论何时列被访问(无论它们被修改与否),审计就开始。这个特性使得 FGA 比触发器具有更多的功能。
另一个优点是 FGA 工具的适用性。有时,在一个视图上定义的 INSTEAD OF 触发器在基表上更新视图;另一个 INSTEAD OF 触发器不能捕获由其它的触发器所作的修改,因此这些修改不能被记录。然而,FGA 是建立在视图或表的基础上的,它能够捕获变化,而不论变化来自哪里 ― 用户语句或触发器。
那么,是否存在触发器比 FGA 更好的情况呢?可能有两种情况:
在变化期间 FGA 的行为
数据始终在变化,因此它有可能变得适用于审计条件 ― 虽然之前它不适用于审计条件,反之亦然。这个问题带来了一些关于 FGA 在不同情况下的行为的有趣问题。考虑我们的例子,其中在 UPDATE 上已经定义了 FGA 策略,条件为 BALANCE >= 3000,审计列是 BALANCE。
第 1 种情况
之前:BALANCE = 1000
用户发出:
update accounts set balance = 1200 where ACCOUNT_NO = ....
旧的和新的 balance 都小于 3,000,审计条件不满足;因此这条语句将 不会 被审计。
第 2 种情况
之前:BALANCE = 1000
用户发出:
update accounts set balance = 3200 where ACCOUNT_NO = ....
新的 balance 大于 3,000,审计条件满足;因此这条语句将 会 被审计。
第 3 种情况
之前:BALANCE = 3200
用户发出:
update accounts set balance = 1200 where ACCOUNT_NO = ....
新的 balance 小于 3,000,但旧的 balance 大于 3,000。因此审计条件满足,这条语句将被审计。
第 4 种情况
用户插入一行,其中有 BALANCE < 3000。
insert into accounts values (9999,1200,'X');
因为 balance 1,200 不满足审计条件,所以这条语句不被审计。如果 balance 列大于或等于 3,000,它将被审计。
第 5 种情况
用户插入一行,其中 balance 的值为空。
insert into accounts (account_no, status) values (9997, 'X');
因为 balance 为空,该列没有任何默认值,所以审计条件不满足(比较 NULL >= 3000 结果为 FALSE),这条语句不会被审计。 重要注意事项 :假设该列 有 一个大于 3,000 的默认值时,这条语句仍然不会被审计,即使插入行的 balance 列值大于 3000。
所有相关的列?
考虑在表 ACCOUNTS 上定义的一个策略,如下:
begin dbms_fga.add_policy ( object_schema => 'ANANDA', object_name => 'ACCOUNTS', policy_name => 'ACCOUNTS_SEL', audit_column => 'ACCOUNT_NO, BALANCE', audit_condition => 'BALANCE >= 3000', statement_types => 'SELECT' ); end;
您可以看到,策略是在 ACCOUNT_NO 和 BALANCE 上定义的。假定帐户 9995 的余额是 3,200,如果用户发出以下语句:
select balance from accounts where account_no = 9995;
这条语句将被审计,因为 balance 列被选中,且余额为 3,200,大于 3,000,满足审计条件。不管这三个列中哪一个被选中,都将触发审计。
在某些情况下,列的组合可能很重要,而不是某个特定的列。例如,如果一个用户想查出在银行的总余额,她发出:
select sum(balance) from accounts;
这条查询几乎没什么害处;它不明确指出帐户所有者和帐户余额。Acme Bank 安全策略可能不会要求审计这条查询。不过,这条查询
select balance from accounts where account_no = 9995
必须被审计;因为它明确地指定了一个帐户。默认地, 所有 语句都被审计(无论使用了什么样的列组合)。这将创建大量不需要的审计线索项目,并可能带来一些空间限制问题。为了限制它们,您可以指定仅当在查询中使用了希望的列组合时才开始审计。当定义策略时,您可以使用一个新的参数:
audit_column_opts => DBMS_FGA.ALL_COLUMNS
这个参数将使策略仅当列 ACCOUNT_NO 和 BALANCE 在查询中都被访问时才创建审计线索项目。例如,以下查询将产生一个审计线索项目。
select account_no, balance from accounts;
但这条查询不会产生审计线索项目。
select account_no from accounts;
使用这个参数将把审计的数量限制在一个更易管理的大小。如果希望采用默认的行为 ― 即任意列被选中时都进行审计,那么您可以对同一参数的使用不同值。
audit_column_opts => DBMS_FGA.ANY_COLUMNS
捕获赋值变量
利用 Oracle Database 10g,其它的有用信息(如在查询中使用的赋值变量的值)可以写到常规的审计线索中。您可以通过设置初始化参数来执行这项任务
audit_trail = DB_EXTENDED
在 FGA 审计线索中,获取赋值变量的值可能有意义也可能没意义。如果您想要停止记录这些值,您可以在 add_policy() 过程中使用另一个参数,如下:
audit_trail => DB
默认情况下,捕获赋值变量,这个参数的值为 DB_EXTENDED。
全部综合在一起
现在您已经了解了 Oracle Database 10g 中的几个新的 FGA 参数,下面让我们看看策略创建脚本的声明现在是什么样子。
下面我们定义对应四种语句类型的四种不同的策略。SELECT 语句的策略显示如下,这里我们选择了不记录赋值变量的值,并仅当列 ACCOUNT_NO 和 BALANCE 在查询中都被使用时才触发审计。
begin dbms_fga.add_policy ( object_schema => 'ANANDA', object_name => 'ACCOUNTS', policy_name => 'ACCOUNTS_SEL', audit_column => 'ACCOUNT_NO, BALANCE', audit_condition => 'BALANCE >= 3000', statement_types => 'SELECT', audit_column_opts => DBMS_FGA.ALL_COLUMNS, audit_trail => DB ); end;
同样,我们将为 INSERT、UPDATE 和 DELETE 语句创建类似的策略。它们可以随意地启用或禁用。
结合常规审计和细粒度审计
在 Oracle Database 10g 中,常规审计也得到了巨大的改进。通过 AUDIT 命令执行常规审计,它现在能够捕获大量其它有用的信息,例如:
您可以看到,在内容和功能方面,常规审计类似于细粒度审计。然而,作为一个数据库管理员,您有兴趣知道所有的审计项目,而不只是一个审计项目。一个新的视图, DBA_COMMON_AUDIT_TRAIL ,结合了常规线索和 FGA 线索。用以下查询来检查它们二者:
select * from dba_common_audit_trail;
这个视图结合了 DBA_AUDIT_TRAIL 和 DBA_FGA_AUDIT_TRAIL,拥有来自每个视图的相关信息。这个视图从数据字典中创建,如下所示。
select 'Standard Audit', SESSIONID, PROXY_SESSIONID, STATEMENTID, ENTRYID, EXTENDED_TIMESTAMP, GLOBAL_UID, USERNAME, CLIENT_ID, Null, OS_USERNAME, USERHOST, OS_PROCESS, TERMINAL, INSTANCE_NUMBER, OWNER, OBJ_NAME, Null, NEW_OWNER, NEW_NAME, ACTION, ACTION_NAME, AUDIT_OPTION, TRANSACTIONID, RETURNCODE, SCN, COMMENT_TEXT, SQL_BIND, SQL_TEXT, OBJ_PRIVILEGE, SYS_PRIVILEGE, ADMIN_OPTION, GRANTEE, PRIV_USED, SES_ACTIONS, LOGOFF_TIME, LOGOFF_LREAD, LOGOFF_PREAD, LOGOFF_LWRITE, LOGOFF_DLOCK, SESSION_CPU from DBA_AUDIT_TRAIL UNION ALL select 'Fine Grained Audit', SESSION_ID, PROXY_SESSIONID, STATEMENTID, ENTRYID, EXTENDED_TIMESTAMP, GLOBAL_UID, DB_USER, CLIENT_ID, EXT_NAME, OS_USER, USERHOST, OS_PROCESS, Null, INSTANCE_NUMBER, OBJECT_SCHEMA, OBJECT_NAME, POLICY_NAME, Null, Null, Null, STATEMENT_TYPE, Null, TRANSACTIONID, Null, SCN, COMMENT$TEXT, SQL_BIND, SQL_TEXT, Null, Null, Null, Null, Null, Null, Null, Null, Null, Null, Null, Null from DBA_FGA_AUDIT_TRAIL
FGA 和常规审计:差异
如果标准审计和细粒度审计在 Oracle Database 10g 中是类似的,您可能要问,在什么情况下,FGA 将是更好的选择?好的,让我们研究一下它们的差异。
通过上面的比较,您将了解为什么可以证明 FGA 在某些情况下很有用。利用 Oracle Database 10g 中的增强的常规审计特性,一些以前认为不可能的任务 ― 例如捕获赋值变量的值 ― 变得十分容易。
http://www.oracle.com/technetwork/cn/topics/security/all-sec-obes-098437-zhs.html
内容转自oracle官方的在线文档。OCM考试中安全部分的内容,主要涉及DBMS_FGA,DBMS_RLS 两个包,FGA主要用于审计,RLS主要用于访问控制。也就是FGA记录了用户如何去查询, RLS控制用户能查询什么信息。目前理解是这样的。