檔案和目錄的訪問控制
一、訪問控制列表
許可權的概念相信你已經不陌生了,那麼如何設定一個檔案的訪問許可權呢?程式設計可不可以實現動態的控制檔案許可權資訊呢?答案是肯定的,.NET可以做到這些。
自由訪問控制列表(Discretionary Access Control List,DACL)(有時縮寫為ACL)是一種 Microsoft Windows NT 和更高版本用於保護資源(例如檔案和資料夾)的機制。DACL包含多個訪問控制項(Access Control Entry,ACE)。訪問控制項將一個主體(通常是一個使用者賬戶或使用者賬戶組)與一個控制資源的使用的規則相關聯。通過DACL 和ACE,可以基於與使用者賬戶關聯的許可權允許或拒絕資源的許可權。例如,可以建立一個ACE,並將其應用於某個檔案的DACL,以阻止除管理員以外的任何人讀取該檔案。
系統訪問控制列表(System Access Control List,SACL)(有時稱為稽核 ACE)是一種控制與資源關聯的稽核訊息的機制。與DACL相似,SACL包含定義指定資源的稽核規則的 ACE。通過稽核ACE,可以記錄訪問資源的成功嘗試或失敗嘗試,但與訪問ACE不同的是,稽核ACE不控制哪些賬戶可以使用某個資源。例如,可以建立一個ACE並將其應用於某個檔案的SACL,以記錄開啟該檔案的所有成功嘗試。
System.Security.AccessControl名稱空間通過一些方便的類(這些類抽象化Windows ACL 安全系統的大部分複雜性)提供對訪問控制列表(ACL)的訪問。此外, System.Security.AccessControl名稱空間還包含幾個提供對Windows ACL安全系統的高階訪問的類。
.NET Framework提供對下列資源的ACL的訪問:加密金鑰、目錄、事件等待控制代碼、檔案、Mutexes、登錄檔項、訊號量。
上述每個資源都有幾個用於建立和修改ACL的類,本節主要關注目錄和檔案的訪問控制。這些類如下:
DirectorySecurity類
該類表示目錄的訪問控制和稽核安全。該類指定系統目錄的訪問許可權以及訪問嘗試的稽核方式。此類將訪問和稽核許可權表示為一組規則,每個訪問規則由一個FileSystemAccessRule物件表示,而每個稽核規則由一個FileSystemAuditRule物件表示。
FileSecurity類
該類指定系統檔案的訪問許可權以及如何稽核訪問嘗試。此類將訪問和稽核許可權表示為一組規則,每個訪問規則由一個 FileSystemAccessRule 物件表示,而每個稽核規則由一個 FileSystemAuditRule 物件表示。使用該類可檢索、新增或更改表示檔案的 DACL 和 SACL 的訪問規則。
DirectorySecurity類和FileSecurity類是對基礎Microsoft Windows檔案安全系統的抽象。在此係統中,每個目錄都有一個自由訪問控制列表(DACL)和一個系統訪問控制列表(SACL),前者控制對目錄的訪問,後者指定要稽核的訪問控制嘗試。FileSystemAccessRule和FileSystemAuditRule 類是對組成DACL和SACL的訪問控制項(ACE)的抽象。
二、新增訪問控制
對檔案和目錄訪問控制的操作基本相同,對於同一種操作本書在通常情況下不重複舉例,讀者可自行實踐。
程式碼清單7-9是一個簡單的示例,用來演示對檔案新增訪問控制。
程式碼清單 7-9 對檔案新增訪問控制
using(FileStream file = new FileStream(@"E:\AclTest\acltest.txt", FileMode.Open, FileAccess.ReadWrite)) { FileSecurity security = file.GetAccessControl(); FileSystemAccessRule rule = new FileSystemAccessRule( new NTAccount(@"XuanHunComputer\xuanhun"), FileSystemRights.Read, AccessControlType.Allow); security.AddAccessRule(rule); file.SetAccessControl(security); }
現在通過分析程式碼清單7-9來了解控制新增單個檔案訪問控制的細節。首先要做的是獲取對檔案的訪問例項,這裡使用FileStream,也許使用File或者FileInfo是你更喜歡的選擇。通過對呼叫GetAccessControl方法來檢索該檔案的安全物件(型別為FileSecurity);除了包含其他內容以外,該物件還包含一組有序的訪問規則,它們共同確定了各種使用者和組對該檔案所具有的權利。在該示例中,將一個新的訪問規則新增到FileSecurity物件中,以便向名為xuanhun的使用者授予檔案的訪問權。在更改生效之前,必須將其持久儲存在儲存器中。最後這個步驟是通過呼叫SetAccessControl方法完成的。
程式碼清單7-9說明了如何向現有檔案分配訪問權,那麼如何在建立檔案的初始就分配許可權呢?這樣做有一個重要的安全原因:可確保安全的物件總是用一些預設的安全語義建立的。預設情況下,分層式資源管理器(例如檔案系統或登錄檔)中的物件從其父物件中繼承它們的安全設定,檔案從它們的父目錄中繼承它們的安全設定。預設權利取決於所建立物件的型別,而且可能不是您所希望的那樣。例如,您很少會有意建立每個人都具有完全訪問許可權的物件,但這卻可能恰好是預設安全設定所指定的許可權。不能簡單地用預設安全設定建立物件並且在以後修改這些設定,產生此問題的原因是:在已經建立物件之後對其加以保護會開啟一個機會視窗(在建立和修改之間),在此期間,該物件可能被劫持。劫持可能導致建立者失去對剛剛所建立物件的控制,這會造成災難性的後果。程式碼清單7-10演示了在如何建立檔案時配置訪問規則。
程式碼清單 7-10 為新建立檔案新增規則
FileSecurity security = new FileSecurity(); FileSystemAccessRule rule = new FileSystemAccessRule( new NTAccount(@"XuanHunComputer\xuanhun"), FileSystemRights.Read, AccessControlType.Allow); security.AddAccessRule(rule); FileStream file = new FileStream( @"M:\temp\sample.txt", FileMode.CreateNew, FileSystemRights.Read, FileShare.None, 4096, FileOptions.None, security);
程式碼清單7-10與程式碼清單7-9執行的是相同的操作,但順序不同,並且無需持久儲存更改(因為物件是全新的)。在建立檔案之前,先建立一個FileSecurity物件,並且用所需的訪問規則填充它。隨後,FileSecurity例項被傳遞給檔案的建構函式,該檔案從一開始就被正確地加以保護。
三、訪問規則
訪問規則有兩種型別:“允許”(allow)和“拒絕”(deny)。可以通過檢查規則的AccessControlType屬性來確定相應規則的型別。按照約定,拒絕規則總是優先於允許規則。因而,如果向某個物件中新增下列兩個規則:“授予每個人讀、寫訪問許可權”和“拒絕Xuanhun寫訪問許可權”,則Xuanhun將被拒絕進行寫訪問。
想要列舉檔案或者目錄的訪問規則時,可以使用如程式碼清單7-11所示的方式。
7-11 列舉檔案訪問規則
class Program { static void Main(string[] args) { string path = @"e:\AclTest\acltest.txt"; FileSecurity security = File.GetAccessControl(path); foreach (FileSystemAccessRule rule in security.GetAccessRules(true, true, typeof(NTAccount))) { Console.WriteLine("{0} {1}---- {2}", rule.AccessControlType == AccessControlType.Allow ? "授權" : "拒絕",rule.IdentityReference.ToString(), rule.FileSystemRights ); } Console.Read(); } }
以上程式碼使用File類的GetAccessControl方法獲取FileSecurity物件,然後通過FileSecurity類的GetAccessRules方法獲取當前檔案的訪問規則。對於目錄的操作與此基本類似,相應地,使用的是DirectorySecurity物件。
GetAccessRules方法有三個引數:第一個引數表示是否要包括為物件顯式設定的訪問規則;第二個引數表示是否要包括繼承的訪問規則;第三個引數表示訪問規則的型別。程式碼清單7-11的執行結果如圖7-9所示。
7-9 列舉檔案訪問規則的執行結果
為方便起見,訪問規則被公開為集合。該集合是隻讀的,因此對它的規則進行的所有修改都必須通過FileSecurity物件的專用方法(例如AddAccessRule、SetAccessRule和RemoveAccessRule)執行。集合內部的規則物件也是不可改變的。要了解為什麼拒絕規則優先於允許規則,必須知道訪問檢查演算法是如何工作的。當執行訪問許可權檢查時,將按照規則在訪問控制列表內部出現的順序對它們進行評估。在程式碼清單7-11中,當檢查使用者Xuanhun的訪問許可權時,會首先評估拒絕Xuanhun讀取訪問的規則,然後再評估授予BUILTIN\Everyone讀取和執行訪問許可權的規則。一旦做出允許或拒絕的決策,評估就將停止。這就是拒絕規則“生效”的原因。如果它們被放置在允許規則之後,則它們不會總是執行它們的預期功能。
和可以新增新的訪問規則一樣,還可以移除現有的訪問規則。但是注意,在從使用者那裡撤回某項許可權和完全拒絕該許可權之間存在差異。例如,假設Xuanhun是“全職僱員”組的成員,並且被設定為:“全職僱員可以讀取檔案”和“Xuanhun具有讀寫訪問許可權”。根據這一方案,撤消Xuanhun的讀取權利會產生下列規則:“全職僱員可以讀取檔案”和“Xuanhun具有寫入訪問許可權”。這恐怕不是您所期望的結果,因為撤消Xuanhun的讀取訪問許可權沒有產生任何效果:Xuanhun仍然可以作為全職僱員獲得讀取訪問許可權。如果你的目標是確保不會將訪問許可權授予Xuanhun,達到該目標的唯一方式是新增一個拒絕規則。此外,如果該物件根本不包含任何訪問規則,那麼每個人都將被拒絕對該物件的所有訪問許可權。
繼承
我們只考慮了不具有子物件的簡單的葉子物件。一旦從葉子物件(例如檔案、訊號量和互斥鎖)轉向容器物件(例如目錄、登錄檔項和Active Directory容器),事情就變得複雜了。額外的複雜性源自以下事實:容器的訪問規則可能被配置為不僅應用於物件本身,而且還應用於它的子物件、子容器或這兩者。這就涉及繼承和傳播設定的領域。
每個訪問規則不是顯式的就是繼承的(用IsInherited屬性來確定),顯式規則是那些已經通過在物件上執行的顯式操作新增到該物件的規則;相反,繼承規則來自於父容器。在使用物件時,只能操縱它的顯式規則。
在向容器中新增新的顯式規則時,可以指定兩組標誌:繼承標誌和傳播標誌。繼承標誌有兩個:容器繼承(Container Inherit,CI)和物件繼承(Object Inherit,OI)。指定容器繼承的規則將應用於當前容器物件的子物件,物件繼承規則應用於葉子子物件。當傳播標誌被設定為None時,這些關係是可傳遞的:它們將跨越當前容器下層次結構的整個子樹,並且應用於該容器的子物件、孫子物件等。
規則的順序是很重要的,因為它確定了優先順序,並最終影響到物件的訪問方式。儘管無法更改預設順序,但明白一組規則將被授予哪個型別的訪問許可權是很重要的。最重要的是,所有繼承規則總是跟在顯式規則後面。這樣,顯式規則總是優先於繼承規則,父規則優先於祖父規則,等等。
父物件和子物件
如果希望物件避開由其父物件給予它的安全語義,會發生什麼情況呢?實際上,確實存在可以宣告“我的父物件的安全設定將不再適用於我”的機制。此時,甚至可以指定是否希望在該情況下使繼承規則保持原樣,但是在父物件的設定發生更改時拒絕“偵聽”,另外,可以徹底清除所有繼承規則。這是通過訪問控制保護實現的,如程式碼清單7-12所示:
7-12 訪問控制保護
using(FileStream file = new FileStream( @"M:\temp\sample.txt", FileMode.Open, FileAccess.ReadWrite)) { FileSecurity security = file.GetAccessControl(); security.SetAccessRuleProtection( true, false ); file.SetAccessControl(security);}
以上程式碼中需要說明的是SetAccessRuleProtection方法,該方法設定或移除與此ObjectSecurity 物件關聯的訪問規則的保護。受保護的稽核規則不會通過繼承被父物件修改。它的第一個引數標識訪問規則是否被繼承,如果為true則不被繼承,否則繼承。第二個引數標識是否保留繼承的訪問規則,如果保留為true,去除則為false。
注意 儘管可以使用該技術避免從父物件那裡收到繼承設定,但沒有辦法收到父物件不打算給予你的繼承設定,傳播只會發生在其ACL沒有受到保護的物件上。可以做的唯一事情就是在父物件改變主意之前獲得繼承設定的快照,因為一旦訪問規則受到保護,它們就將保持這個狀態,並且父物件無法重寫它們。
所有者
“所有者”(owner)的概念對於物件安全性是很特殊的。所有者被賦予了特殊的權力,即使與物件相關聯的規則禁止使用者訪問該物件,但如果該使用者是所有者,則他仍然可以重寫現有規則,並重新獲得對該物件的控制。完成該操作的過程與訪問規則的常規操作過程沒有什麼不同。安全物件還允許更改所有者,但是作業系統將禁止其他人執行該操作。通常,為了更改所有者,必須具有物件的TakeOwnership許可權或者具有特殊的“取得所有權”(Take Ownership)特權更改所有者如下所示(假設你具有這樣做的權利):
FileSecurity security = file.GetAccessControl(); security.SetOwner(new NTAccount(@"Administrators\Xuanhun")); file.SetAccessControl(security);
以上程式碼中使用SetOwner方法來制定當前檔案的所有者為NTAccount型別使用者Xuanhun。
此外,還可以檢視物件的當前所有者是誰,或者請求將所有者作為安全識別符號或Windows NT賬戶物件返回,如以下程式碼所示:
SecurityIdentifier sid = (SecurityIdentifier)security.GetOwner(typeof(SecurityIdentifier)); Console.WriteLine(sid.ToString()); NTAccount nta = (NTAccount)security.GetOwner(typeof(NTAccount)); Console.WriteLine(nta.ToString());
以上程式碼使用兩種形式來檢視所有者:一種是SecurityIdentifier物件,另一種是NTAccount物件。SecurityIdentifier類表示一個安全識別符號 (SID) 併為 SID 提供封送處理和比較操作。NTAccount類表示一個使用者或組賬戶。
四、稽核規則
到目前為止,只是討論了訪問控制規則,它們構成了物件的DACL。DACL可以由物件的所有者任意更改,還可以由所有者已經給予其更改DACL許可權的任何人更改。物件的安全描述符包含另一個規則列表,稱為系統訪問控制列表(System Access Control List,SACL),該列表將控制系統對物件執行哪個型別的稽核。
稽核是一種具有安全敏感性的操作。在Windows中,稽核只能由本地安全機構(Local Security Authority,LSA)生成,因為LSA是唯一允許向安全事件日誌(這裡儲存了稽核)中寫入項的元件。安全稽核是一項非常嚴謹的業務,可以在計算機法庭中根據事實分析誰做了什麼事情,以及誰試圖在系統中做什麼事情。很多組織都長年保留它們的稽核日誌。不用說,規定對哪些專案進行稽核的設定通常都受到嚴格的管理控制。如果執行該節中的程式碼並且遇到UnauthorizedAccessException訊息,可能是因為執行時所在的賬戶不包含“安全特權”(Security Privilege)。為了能夠修改甚至分析SACL,必須由本地計算機策略向你的賬戶分配這一強大的特權。儘管有這些可怕的警告,但在具有必要的特權之後,讀取和操作物件的稽核設定在所有方面都類似於修改訪問控制設定。程式碼清單7-13是一個操作稽核規則的簡單示例。
程式碼清單7-13 操作稽核規則
using(FileStream file = new FileStream( @"M:\temp\sample.txt",FileMode.Open, FileAccess.ReadWrite)) { FileSecurity security = file.GetAccessControl(); FileSystemAuditRule rule = new FileSystemAuditRule( new NTAccount( @"FABRIKAM\Full_Time_Employees"), FileSystemRights.Write, AuditFlags.Failure); security.AddAuditRule(rule); file.SetAccessControl(security) }
與之前的程式碼示例不同的是,本示例使用一個新的FileSystemAuditRule類。該類表示基礎訪問控制項(ACE)的抽象,該訪問控制項指定使用者賬戶、要提供的訪問的型別(讀、寫等),以及是否要執行稽核。此類還可以指定如何從物件繼承稽核規則以及將稽核規則傳播到物件。
若要在 Microsoft Windows NT 上允許檔案或目錄稽核,必須在自己的計算機上啟用Audit Access Security策略。預設情況下,該策略設定為No Auditing。
啟用 Audit AccessSecurity 策略的步驟如下:
步驟 1 開啟 Local Security Settings Microsoft 管理控制檯 (MMC) 管理單元,定位於 Administrative Tools 資料夾中。
步驟 2 展開 Local Policies 資料夾,左擊 Audit Policy 資料夾。
步驟 3 在該 MMC 管理單元的右窗格上雙擊 Audit object access 項,或右擊並選擇屬性選項以顯示 Audit object access Properties dialog。
步驟 4 選中Success 或 Failure 框以記錄成功或失敗。
注意 使用者賬戶的稽核規則需要同一使用者賬戶的對應訪問規則。
如程式碼清單7-13所示,需要使用FileSystemAuditRule類建立新的稽核規則,然後使用FileSecurity或DirectorySecurity類可持久儲存此規則。
稽核設定被表示為稽核規則。可以指定你想要稽核的安全主體(使用者或組)的名稱、感興趣的訪問許可權型別(例如讀取、寫入等)以及你是希望在授予、拒絕訪問許可權還是在執行這兩種操作時生成稽核。例如,在程式碼清單7-13中,每當全職僱員被拒絕對某個檔案或給定父目錄下的目錄進行寫入訪問時,系統都將生成稽核。繼承標誌、傳播標誌和保護設定對稽核規則的作用方式與它們對訪問控制規則的作用方式完全相同。
程式設計是個人愛好