Oracle_Label_Security[轉]

cnbird發表於2013-04-17

前言:
在Oracle9i中有一個元件稱為Oracle Label Security,這個元件實現了基於自定義策略而對資料庫中的表甚或是整個Schema提供行級安全性功能。實際上Oracle Label Security是在Oracle8.1.7中提出的,在9i版本中功能得到了大幅度增強。

本文通過一個簡單的例子對Oracle Label Security的功能作初步的探討,本文使用的環境是Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 for Windows 32 bit。

流程:
首先我們瞭解一下實現Oracle Label Security的大體流程。
通過Oracle提供的一系列儲存過程,先建立一個policy,然後在policy中建立level,compartment,group,之後通過這些定義好的level,compartment,group再定義label,然後將policy繫結到某張表或者某個schema,最後再給相應的使用者設定label。

其中牽涉到幾個名詞,解釋一下:
Policy:就是安全策略,一個安全策略是level,compartment,group,label的集合。
Level:等級,這是最基礎的安全控制等級,必須設定。
Compartment:分隔(這不是官方翻譯),提供第二級的安全控制,是可選的。
Group:組(這不是官方翻譯),提供第三級的安全控制,是可選的。
Label:標籤,最終體現到每一行上的安全標籤,必須設定。只有使用者被賦予的標籤和此行上的標籤相同或者等級更高的時候,該行才能夠被使用者存取。

方法:
然後再來了解一下Oracle實現Label Security的大體方法。
在設定完Label以後,通過SQL Trace,我們可以看到使用者發出的select語句執行計劃有了變化,即使是全表掃描,也會多出一個filter的操作。

  1. select *
  2. from
  3.  documents
  4.  
  5. Rows     Row Source
    Operation
  6. —–—  —————————————————
  7.       5 
    FILTER  (cr=7 pr=0
    pw=0
    time=184
    us)
  8.       5  
    TABLE ACCESS
    FULL DOCUMENTS
    (cr=7
    pr=0
    pw=0
    time=131
    us)

通過檢視raw trace file可以看到Oracle在parse這個SQL的時候,自動新增了一個條件:
begin :con := LBAC_RLS.READ_FILTER(:sn, : on); end;
而這個條件的具體操作在trace file中也有儲存,此處就不多說了。總之,通過trace我們知道對於設定了label的表的select操作,Oracle自動新增了條件來保證使用者只能選擇到自己能夠看到的記錄。

那麼,對於DML(insert,update,delete)操作呢?其實,Oracle是自動在表上新增了觸發器,這一點我們可以在設定完label以後,通過dba_triggers檢視驗證,新增的是INSERT AFTER EACH ROW,UPDATE AFTER EACH ROW和DELETE BEFORE EACH ROW三個trigger,Oracle的很多功能在內部其實都是通過trigger完成,比如高階複製同樣如此,但是Oracle一直聲稱這些trigger是內部實現的,所以比使用者自定義的trigger效率要高。當然,內部trigger和自定義trigger在實現機制上是否真的不同,還有待驗證。

目的:
下面我們的例子將實現這樣的功能。

給DOCUMENTS表中的資料設定8個標籤:
第一個標籤是PUBLIC,表示該類記錄所有人都可以讀取。
第二個標籤是HR部門的員工才可以讀取的。
第三個標籤是HR部門並且是WEST地區的員工才可以讀取的。
第四個標籤是HR部門並且是EAST地區的員工才可以讀取的。
第五個標籤是FIN部門的員工才可以讀取的。
第六個標籤是FIN部門並且是WEST地區的員工才可以讀取的。
第七個標籤是FIN部門並且是EAST地區的員工才可以讀取的。
第八個標籤是無論哪個部門,無論哪個地區,都可以讀取。

這8個標籤用於控制DOCUMENTS表中的所有記錄,記錄一共分成5種型別,這裡我們簡化為5條記錄。

  1. SQL>
    select *
    from kamus.documents;
  2.  
  3.      DOCID DOCNAME
  4. ——–— —————————————-
  5.          1 SHARE_WARE
  6.          2 INTERNAL_HR_WEST
  7.          3 INTERNAL_HR_EAST
  8.          4 INTERNAL_FIN_EAST
  9.          5 INTERNAL_FIN_WEST

如果一個使用者被賦予了第一個標籤,那麼他只能讀取DOCID=1的第一條記錄。
如果一個使用者被賦予了第二個標籤,那麼他能讀取1,2,3這三條記錄。
如果一個使用者被賦予了第三個標籤,那麼他能讀取1,2這兩條記錄。
如果一個使用者被賦予了第四個標籤,那麼他能讀取1,3這兩條記錄。
如果一個使用者被賦予了第五個標籤,那麼他能讀取1,4,5這三條記錄。
如果一個使用者被賦予了第六個標籤,那麼他能讀取1,5這兩條記錄。
如果一個使用者被賦予了第七個標籤,那麼他能讀取1,4這兩條記錄。
如果一個使用者被賦予了第八個標籤,那麼他能讀取所有的5條記錄。

因為是初次探討Label Security功能,所以可能在標籤設定上還有更加簡化的方法,也希望大家能夠在看了本文以後思考一下,看看是否能更加簡單地實現相同的功能。

步驟:
我們需要用到3個使用者,一個是擁有DOCUMENTS表的KAMUS使用者,一個是用於設定OLS策略的LBACSYS使用者,另外一個是不受OLS策略制約的SYS使用者(用來方便的插入和更新測試資料)。
1. 安裝Oracle Label Security
在安裝資料庫軟體的時候必須保證選擇了Oracle Label Security元件,否則所有功能都無法使用。如果當時沒有選擇,可以重新執行Universal Installer進行安裝。
LBACSYS使用者可以利用$ORACLE_HOME/rdbms/admin/catols.sql建立。

  1. SQL>
    CONN / AS
    SYSDBA;
  2. SQL> @?/rdbms/admin/catols

在這個指令碼的最後會自動關閉資料庫,所以請不要在生產庫上直接測試。

再次開啟資料庫,就可以使用LBACSYS使用者登入了,預設密碼就是lbacsys,如果投放在生產環境中,請立刻修改預設密碼。

2. 建立安全策略

  1. SQL>
    CONN lbacsys/lbacsys
  2. SQL> EXEC sa_sysdba.create_policy(`DOC_POLICY`,
    `DOC_LABEL`);

第一個引數DOC_POLICY是策略的名稱,第二個引數DOC_LABEL是即將新增到DOCUMENTS表中的用於儲存標籤的欄位名,這個欄位將在後面apply table policy的時候自動完成,所以不必預先新增。

可以從DBA_SA_POLICIES檢視中查詢安全策略的情況。

3. 定義Level

  1. SQL>
    EXEC sa_components.create_level(`DOC_POLICY`,
    1000,`PUBLIC`,
    `Public Level`);
  2. SQL> EXEC sa_components.create_level(`DOC_POLICY`,
    2000,`INTERNAL`,
    `Internal Level`);

第一個引數是上一步建立的安全策略的名字。第二個引數是Level的等級,數字越大表示許可權越高,比如此處具有INTERNAL等級的就可以同時檢視有PUBLIC等級的資料。第三個引數是Level的短名,隨便定義。第四個引數是Level的長名,只是起到一個說明的作用,隨便定義。

可以從DBA_SA_LEVELS檢視中查詢安全等級的情況。

4. 定義Compartment。
本步操作是可選項,僅僅使用上一步中的Level就已經可以控制資料的行級安全性了,但是如果要實現更加複雜的控制,就可能需要定義Compartment和下一步的Group。

  1. SQL>
    EXEC sa_components.create_compartment(`DOC_POLICY`,
    200,`FIN`,
    `FINANCE`);
  2. SQL> EXEC sa_components.create_compartment(`DOC_POLICY`,
    100,`HR`,
    `HUMAN_R`);

引數依次是安全策略名,Compartment數字,短名,長名,此處的Compartment數字不涉及到許可權的高低,僅僅是一個識別符號而已。
可以從DBA_SA_COMPARTMENTS檢視中查詢安全間隔的情況。

5. 定義Group。

  1. SQL>
    EXEC sa_components.create_group(`DOC_POLICY`,
    10,`ALL`,
    `ALL_REGIONS`);
  2. SQL> EXEC sa_components.create_group(`DOC_POLICY`,
    20,`WEST`,
    `WEST_REGIONS`);
  3. SQL> EXEC sa_components.create_group(`DOC_POLICY`,
    30,`EAST`,
    `EAST_REGIONS`);

引數依次是安全策略名,Group數字,短名,長名,此處的Group數字不涉及到許可權的高低,僅僅是一個識別符號而已。

可以從DBA_SA_GROUPS檢視中查詢安全組的情況。

注意,create_group儲存過程有第5個引數,是parent_name,通過這個引數可以實現幾乎無限制的多層許可權控制,本文就不再深入了。

6. 建立Label。

  1. SQL>
    EXEC sa_label_admin.create_label(`DOC_POLICY`,
    `10000`,
    `PUBLIC`,
    TRUE);
  2. SQL> EXEC sa_label_admin.create_label(`DOC_POLICY`,
    `30000`,
    `INTERNAL:HR:WEST,EAST`,
    TRUE);
  3. SQL> EXEC sa_label_admin.create_label(`DOC_POLICY`,
    `35000`,
    `INTERNAL:HR:WEST`,
    TRUE);
  4. SQL> EXEC sa_label_admin.create_label(`DOC_POLICY`,
    `36000`,
    `INTERNAL:HR:EAST`,
    TRUE);
  5. SQL> EXEC sa_label_admin.create_label(`DOC_POLICY`,
    `40000`,
    `INTERNAL:FIN:WEST,EAST`,
    TRUE);
  6. SQL> EXEC sa_label_admin.create_label(`DOC_POLICY`,
    `45000`,
    `INTERNAL:FIN:WEST`,
    TRUE);
  7. SQL> EXEC sa_label_admin.create_label(`DOC_POLICY`,
    `46000`,
    `INTERNAL:FIN:EAST`,
    TRUE);
  8. SQL> EXEC sa_label_admin.create_label(`DOC_POLICY`,
    `50000`,
    `INTERNAL:FIN,HR:EAST,WEST`,
    TRUE);

建立了上文提到的8個label。

引數依次是安全策略名,Label Tag,Label值,是否為data label。
其中Label Tag必須是不同於系統中任何策略number的數字。
Label值是最關鍵的地方,通過組合前面幾步中定義的level,compartment,group來設定標籤,各個級別之間用冒號(:)隔開,一個級別中的多個元素用逗號(,)隔開。
是否為data label是一個布林值,只有為TRUE的時候,這個標籤才可以用於控制表資料的安全性。

可以從DBA_SA_LABELS檢視中查詢安全標籤的情況。

7. 將策略賦予表。。

  1. SQL>
    EXEC sa_policy_admin.apply_table_policy
  2. > ( policy_name    =>
    `DOC_POLICY`
  3. > , schema_name    =>
    `KAMUS`
  4. > , table_name     =>
    `DOCUMENTS`
  5. > , table_options  =>
    `LABEL_DEFAULT, READ_CONTROL,WRITE_CONTROL,HIDE`);

前三個參數列示我們將DOC_POLICY策略附加到KAMUS使用者的DOCUMENTS表上,執行這步操作的時候,Oracle會自動將第二步中定義的列新增到表中,如果這步執行成功,我們立刻用KAMUS使用者檢索DOCUMENTS表,會發現一條記錄都沒有了,這說明Label Security已經起作用了。

第四個引數用於設定策略如何控制表的安全性。LABEL_DEFAULT表示如果以後一個使用者新增資料的時候沒有指定Label那麼將會使用該使用者的default session label(這個default值在下面一步的使用者Label設定中定義),READ_CONTROL,WRITE_CONTROL表示對於表的讀寫操作都受到安全策略的制約,HIDE表示不在desc表結構的時候顯示DOC_LABEL列名,如果想要顯示就省略HIDE字樣。

注意,一旦apply策略完成,那麼要修改table_options的值,比如想把HIDE去掉,那麼就必須先用sa_policy_admin.remove_table_policy函式刪除policy定義,然後重新apply。

8. 將Label賦予使用者。
使用sa_user_admin.set_user_labels儲存過程來將label賦予使用者,這個儲存過程有不少引數,但是必須輸入的只有policy_name,user_name,max_read_label三項,其它引數如果省略的話,都有預設值。
比如def_label引數(使用者新增資料的時候沒有指定Label時的預設Label)如果沒有設定,那麼預設為跟max_read_label相同。

我們通過給KAMUS使用者賦予不同的Label,來完成測試的目的。每次用LBACSYS使用者設定完KAMUS使用者的label,KAMUS使用者都必須重新登入一次,設定才會生效。

  1. –如果一個使用者被賦予了第一個標籤,那麼他只能讀取DOCID=1的第一條記錄。
  2. SQL> EXEC sa_user_admin.set_user_labels(policy_name=>
    `DOC_POLICY`,user_name =>`KAMUS`,max_read_label
    =>
    `PUBLIC`);
  3.  
  4. SQL> conn kamus/kamus
  5. Connected to
    Oracle Database
    10g
    Enterprise
    Edition Release
    10.2.0.1.0 
  6. Connected as
    kamus
  7.  
  8. SQL> select *
    from documents;
  9.  
  10.      DOCID DOCNAME                                   
    DOC_LABEL
  11. ——–— —————————————- ———–
  12.          1 SHARE_WARE                                    
    10000
  13.  
  14. –如果一個使用者被賦予了第二個標籤,那麼他能讀取123這三條記錄。
  15. SQL> EXEC sa_user_admin.set_user_labels(policy_name=>
    `DOC_POLICY`,user_name =>`KAMUS`,max_read_label
    =>
    `INTERNAL:HR:EAST,WEST`);
  16.  
  17. SQL> conn kamus/kamus
  18. Connected to
    Oracle Database
    10g
    Enterprise
    Edition Release
    10.2.0.1.0 
  19. Connected as
    kamus
  20.  
  21. SQL> select *
    from documents;
  22.  
  23.      DOCID DOCNAME
  24. ——–— —————————————-
  25.          1 SHARE_WARE
  26.          2 INTERNAL_HR_WEST
  27.          3 INTERNAL_HR_EAST
  28.  
  29. –如果一個使用者被賦予了第三個標籤,那麼他能讀取12這兩條記錄。
  30. SQL> EXEC sa_user_admin.set_user_labels(policy_name=>
    `DOC_POLICY`,user_name =>`KAMUS`,max_read_label
    =>
    `INTERNAL:HR:WEST`);
  31.  
  32. SQL> conn kamus/kamus
  33. Connected to
    Oracle Database
    10g
    Enterprise
    Edition Release
    10.2.0.1.0 
  34. Connected as
    kamus
  35.  
  36. SQL> select *
    from documents;
  37.  
  38.      DOCID DOCNAME
  39. ——–— —————————————-
  40.          1 SHARE_WARE
  41.          2 INTERNAL_HR_WEST
  42.  
  43. –如果 …… 此處省略中間的測試,大同小異。
  44.  
  45. –如果一個使用者被賦予了第八個標籤,那麼他能讀取所有的5條記錄。
  46. SQL> EXEC sa_user_admin.set_user_labels(policy_name=>
    `DOC_POLICY`,user_name =>`KAMUS`,max_read_label
    =>
    `INTERNAL:FIN,HR:EAST,WEST`);
  47.  
  48. SQL> conn kamus/kamus
  49. Connected to
    Oracle Database
    10g
    Enterprise
    Edition Release
    10.2.0.1.0 
  50. Connected as
    kamus
  51.  
  52. SQL> select *
    from documents;
  53.  
  54.      DOCID DOCNAME
  55. ——–— —————————————-
  56.          1 SHARE_WARE
  57.          2 INTERNAL_HR_WEST
  58.          3 INTERNAL_HR_EAST
  59.          4 INTERNAL_FIN_EAST
  60.          5 INTERNAL_FIN_WEST

至此,這個Oracle Label Security 的實驗基本上是完成了,達到了我們預先計劃的目標。

其它:
Label Security對於exp同樣具有安全控制作用,但是並不全面。常規路徑的EXP受到了安全控制。

D:Temp>exp kamus/kamus tables=documents file=d.dmp
 
Export: Release 10.2.0.1.0 – Production on 星期四 1月 11 22:08:48 2007
 
Copyright (c) 1982, 2005, Oracle.  All rights reserved.
 
連線到: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 – Production
With the Partitioning, Oracle Label Security, OLAP and Data Mining options
已匯出 ZHS16GBK 字符集和 AL16UTF16 NCHAR 字符集
 
即將匯出指定的表通過常規路徑…
EXP-00079: 表 “DOCUMENTS” 中的資料是被保護的。常規路徑只能匯出部分表。
. . 正在匯出表                       DOCUMENTS匯出了           2 行
EXP-00091: 正在匯出有問題的統計資訊。
匯出成功終止, 但出現警告。

但是隻要加上direct=y這樣用直接路徑匯出,就跳過了Label Security的控制。

D:Temp>exp kamus/kamus tables=documents file=d.dmp direct=y
 
Export: Release 10.2.0.1.0 – Production on 星期四 1月 11 22:08:53 2007
 
Copyright (c) 1982, 2005, Oracle.  All rights reserved.
 
連線到: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 – Production
With the Partitioning, Oracle Label Security, OLAP and Data Mining options
已匯出 ZHS16GBK 字符集和 AL16UTF16 NCHAR 字符集
 
即將匯出指定的表通過直接路徑…
. . 正在匯出表                       DOCUMENTS匯出了           5 行
成功終止匯出, 沒有出現警告。

而關於效能方面,如果表中資料量很大,那麼出於效能考慮,可能需要在Label列上(本例中的DOC_LABEL)新增合適的索引,根據cardinality的多少,選擇B-Tree或者bitmap索引。

提問:
大家可能注意到在第5步定義Group的時候,我定義了3個組,其中ALL組一直沒有用到,那麼現在的問題就是:
新建立一個Label:

  1. EXEC
    sa_label_admin.create_label(`DOC_POLICY`,
    `20000`,
    `INTERNAL:HR:ALL`,
    TRUE);

新插入一條資料

  1. insert
    into kamus.documents
    values(6,`INTERNAL_HR_ALL`,20000);

而此時KAMUS使用者的max_read_label是最高等級的’INTERNAL:FIN,HR:EAST,WEST’
那麼select * from documents;會有幾行資料返回?

注意:插入資料,可以通過直接指定Label Tag數值或者使用CHAR_TO_LABEL函式,比如上面的insert語句等值於:

  1. insert
    into kamus.documents
    values(6,`INTERNAL_HR_ALL`,
    CHAR_TO_LABEL(`DOC_POLICY`,`
    INTERNAL:HR:ALL
    `));

結論:
通過Oracle Label Security策略可以很方便地在資料庫這個層面上完成對於應用資料的安全性保護,個人認為跟基於VPD和SYS_CONTEXT的行級安全保護方法相比,OLS在分層控制方面更有效,設定更簡單,而後者則因為手動編寫VPD策略函式,所以具有更大的靈活性。但是無論是使用OLS還是VPD策略函式,都無法防止SYSDBA使用者檢視業務資料,針對此問題,Oracle的解決方案是Oracle Data Vault。

後續:
Oracle資料庫安全領域有很多我還沒有接觸到的東西需要學習,下面一篇文章我將會介紹Oracle10g的新增元件Oracle Data Vault ,敬請期待。


相關文章