現實問題的細粒度審計,第 3 部分
2024-07-21 02:35:29
供稿:網友
在本系列的前兩個部分中,我向您介紹了細粒度審計 (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 通過一個自動事務來插入審計線索,這種事務在它自己的上下文中提交。當 DML 語句失敗或被回滾時,插入的線索記錄不被回滾。假如用戶更新了一些東西但沒有提交,則不執行修改,但不管怎樣,將創建審計線索。這可能導致在審計線索中產生幾個虛假的實際項目 — 一種不希望出現的潛在情況。隨后使用通過閃回查詢捕捉的 SCN 號碼對表進行分析將可能揭露這個問題,但這個過程可能非常復雜。但假如這種風險是不可接受的,那么基于觸發器的方法將優于 FGA 成為首選。
FGA 記錄用戶發出的 SQL 語句和 SCN 號碼,但不記錄在修改之前和之后的值。必須使用一個單獨的工具來從表中通過閃回查詢取出這些值。因為閃回查詢依靠于 UNDO 段中包含的信息(這些信息是有限的),因此這個工具可能不會從過去很久的時間點上取出舊的值?;谟|發器的方法在源數據上捕捉變化,因此保證舊的值和新的值都有記錄。
在變化期間 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',
obje