發(fā)布日期: 4/1/2004 | 更新日期: 4/1/2004
摘要:本文概述了 Microsoft .NET 框架安全系統(tǒng)的基本特性,包括對(duì)于動(dòng)態(tài)下載和執(zhí)行模式以及遠(yuǎn)程執(zhí)行模式,限制代碼在嚴(yán)格約束的、管理員定義的安全上下文中運(yùn)行的能力。(21頁(yè)打印頁(yè))
Demien Watkins 博士,項(xiàng)目 42Sebastian Lange,Microsoft Corporation
2002 年 1 月
本頁(yè)內(nèi)容
簡(jiǎn)介
傳統(tǒng)的程序開(kāi)發(fā)模式中,管理員通常將軟件安裝到本地磁盤(pán)的一個(gè)固定位置;當(dāng)從這種模式轉(zhuǎn)向支持動(dòng)態(tài)下載和執(zhí)行、甚至支持遠(yuǎn)程執(zhí)行的環(huán)境時(shí),安全性是要考慮的一個(gè)最重要的因素。為了支持這種模式,Microsoft .NET 框架提供了一個(gè)強(qiáng)健的安全系統(tǒng),該系統(tǒng)可以限制代碼在嚴(yán)格約束的、管理員定義的安全上下文中運(yùn)行。本文研究了 .NET 框架中的一些基本的安全性特性。
許多安全模型將安全性與用戶和它們的組(或角色)關(guān)聯(lián)起來(lái)。這就意味著,用戶和代表這些用戶運(yùn)行的所有代碼或者被允許對(duì)重要資源執(zhí)行操作,或者不被允許執(zhí)行操作。在大多數(shù)操作系統(tǒng)中安全性都是按照這種模型構(gòu)建的。.NET 框架提供了一種開(kāi)發(fā)人員定義的安全模型,稱為基于角色的安全性,它也是按照這種類似的結(jié)構(gòu)運(yùn)行的?;诮巧陌踩缘闹饕橄笫?Principals 和 Identity。此外,.NET 框架也在代碼上提供了安全性,這種安全性稱為代碼訪問(wèn)安全性(也稱為基于證據(jù)的安全性)。使用代碼訪問(wèn)安全性,某個(gè)用戶可能會(huì)獲得信任以訪問(wèn)某個(gè)資源,但是如果用戶執(zhí)行的代碼不受信任,則訪問(wèn)資源也會(huì)被拒絕。與基于特定用戶的安全性對(duì)比,基于代碼的安全性是允許安全性得以在可移動(dòng)代碼 (mobile code) 上體現(xiàn)的一個(gè)基本工具。可移動(dòng)代碼可能會(huì)由任意數(shù)量的用戶下載和執(zhí)行,而所有這些用戶在開(kāi)發(fā)時(shí)都是不了解的。代碼訪問(wèn)安全性主要集中于一些核心的抽象,它們是:證據(jù)、策略和權(quán)限。基于角色的安全性和代碼訪問(wèn)安全性的安全抽象是用 .NET 框架類庫(kù)中的類型來(lái)表示的,而且是用戶可擴(kuò)展的。這里有兩個(gè)值得注意的挑戰(zhàn):以一致和連貫的方式向一些編程語(yǔ)言公開(kāi)安全模型,以及保護(hù)和公開(kāi) .NET 框架類庫(kù)中代表資源(使用這些資源可能會(huì)導(dǎo)致安全性破壞)的類型。
.NET 框架安全系統(tǒng)是在傳統(tǒng)操作系統(tǒng)安全性上層運(yùn)行的。這種方式在操作系統(tǒng)安全性上又增加了一層更具表現(xiàn)力和可擴(kuò)展的安全性。這兩層安全性相互補(bǔ)充。(操作系統(tǒng)安全系統(tǒng)也可以將一些責(zé)任委托給托管代碼的公共語(yǔ)言運(yùn)行庫(kù)安全系統(tǒng),因?yàn)樵撨\(yùn)行庫(kù)安全系統(tǒng)比傳統(tǒng)的操作系統(tǒng)安全性粒度更細(xì),可配置性也更強(qiáng)。)
本文提供了 .NET 框架安全性方面的一個(gè)概述,具體講述了基于角色的安全性、驗(yàn)證、代碼訪問(wèn)安全性和堆棧審核方面的內(nèi)容。并且使用一些小的編程示例揭示一些概念。本文沒(méi)有論及其他的運(yùn)行庫(kù)安全工具,如加密和獨(dú)立存儲(chǔ)。
順便提一下,本文通常描述的是上面這些安全特性的默認(rèn)行為。然而 .NET 框架安全系統(tǒng)具有極強(qiáng)的可配置性和極強(qiáng)的可擴(kuò)展性。這就是該系統(tǒng)的一個(gè)主要優(yōu)點(diǎn),但遺憾的是,在這篇概念性描述的文章中不能詳細(xì)地對(duì)這一點(diǎn)進(jìn)行討論了。
執(zhí)行概述
運(yùn)行庫(kù)既執(zhí)行托管代碼,又執(zhí)行非托管代碼。托管代碼是在運(yùn)行庫(kù)的控制下執(zhí)行的,因此可以訪問(wèn)運(yùn)行庫(kù)提供的服務(wù),如內(nèi)存管理、實(shí)時(shí) (JIT) 編譯,以及本文涉及的最重要的安全性服務(wù)(如安全策略系統(tǒng)和驗(yàn)證)。非托管代碼是經(jīng)過(guò)編譯、要運(yùn)行在特定硬件平臺(tái)上的代碼,它不可直接利用運(yùn)行庫(kù)。然而,當(dāng)語(yǔ)言編譯器生成托管代碼時(shí),該編譯器的輸出是以 Microsoft 中間語(yǔ)言 (MSIL) 表示的。MSIL 通常被描述為一種用于抽象的、基于堆棧計(jì)算機(jī)、面向?qū)ο蟮膫螀R編語(yǔ)言。之所以說(shuō) MSIL 是面向?qū)ο蟮?,是因?yàn)樗幸恍┲С置嫦驅(qū)ο蟾拍畹闹噶?,如?duì)象分配 (newobj) 和虛函數(shù)調(diào)用 (callvirt)。之所以說(shuō)是抽象的計(jì)算機(jī),是因?yàn)?MSIL 不依賴于任何特定的平臺(tái)。就是說(shuō),它對(duì)于在其上運(yùn)行的硬件不會(huì)作出任何假設(shè)。它之所以是基于堆棧的,是因?yàn)閺谋举|(zhì)看,MSIL 是通過(guò)向堆棧壓入 (push) 值和從中彈出 (pop) 值以及調(diào)用方法來(lái)執(zhí)行的。MSIL 通常會(huì)在執(zhí)行前被實(shí)時(shí)編譯為本機(jī)代碼。(MSIL 也可以在運(yùn)行該代碼前編譯為本機(jī)代碼。這有助于縮短程序集的啟動(dòng)時(shí)間,但是 MSIL 代碼通常是在方法級(jí)別進(jìn)行 JIT 編譯的。)
驗(yàn)證
在運(yùn)行庫(kù)會(huì)進(jìn)行兩種形式的驗(yàn)證:MSIL 驗(yàn)證和程序集元數(shù)據(jù)驗(yàn)證。運(yùn)行庫(kù)中的所有類型都指定了它們將要實(shí)現(xiàn)的協(xié)定,這些信息將作為元數(shù)據(jù)與 MSIL 一起保留在托管 PE/COEFF 文件中。例如,如果一個(gè)類型指定它從另一個(gè)類或接口進(jìn)行繼承,這就表明它將要實(shí)現(xiàn)一些方法,這就是一個(gè)協(xié)定。協(xié)定也可以與可見(jiàn)性聯(lián)系起來(lái)。例如,可以將類型聲明為從它們的程序集公開(kāi)(導(dǎo)出)或其他的內(nèi)容。因?yàn)轭愋桶踩荒芨鶕?jù)它們的協(xié)定訪問(wèn)類型,所以就此而言,它是代碼的一個(gè)屬性??梢则?yàn)證 MSIL 以證明它是類型安全的。驗(yàn)證是 .NET 框架安全系統(tǒng)中的一個(gè)基本構(gòu)造塊,目前只在托管代碼上執(zhí)行驗(yàn)證。因?yàn)榉峭泄艽a不可由運(yùn)行庫(kù)進(jìn)行驗(yàn)證,所以由運(yùn)行庫(kù)執(zhí)行的非托管代碼必須是完全受信任的。
為了理解 MSIL 驗(yàn)證,關(guān)鍵在于理解如何對(duì) MSIL 進(jìn)行分類。MSIL 分為下列類型:無(wú)效的 MSIL、有效的 MSIL、類型安全的 MSIL 以及可驗(yàn)證的 MSIL。
注 應(yīng)該指出的是,下面的定義比標(biāo)準(zhǔn)定義提供了更多信息。有關(guān)更加準(zhǔn)確的定義版本,請(qǐng)參閱其他文檔,如 ECMA standard:
? |
無(wú)效 MSIL 是 JIT 編譯器不能為它生成本機(jī)表示的 MSIL。例如,不可將包含無(wú)效操作碼的 MSIL 翻譯成本機(jī)代碼。另一個(gè)示例是跳轉(zhuǎn)指令,該指令的目標(biāo)是操作數(shù)的地址,而不是操作碼。
|
? |
有效 MSIL 可以被認(rèn)為是滿足 MSIL 語(yǔ)法的所有 MSIL,因此它可以用本機(jī)代碼表示。這種分類包括這樣的 MSIL,即,使用非類型安全形式的指針?biāo)惴ǐ@取對(duì)類型成員的訪問(wèn)的 MSIL。
|
? |
類型安全 MSIL 只通過(guò)它們的向公眾公開(kāi)的協(xié)定與類型交互。試圖從一個(gè)類型中訪問(wèn)另一個(gè)類型的私有成員的 MSIL 就不是類型安全的。
|
? |
可驗(yàn)證 MSIL 是可以通過(guò)一個(gè)驗(yàn)證算法來(lái)證明是類型安全的 MSIL。驗(yàn)證算法比較保守,因此一些類型安全的 MSIL 可能不會(huì)通過(guò)驗(yàn)證??沈?yàn)證 MSIL 自然既是類型安全的又是有效的,當(dāng)然不是無(wú)效的。
|
除了類型安全檢查之外,運(yùn)行庫(kù)中的 MSIL 驗(yàn)證算法還會(huì)檢查堆棧上溢/下溢的發(fā)生、異常處理工具的正確使用以及對(duì)象初始化。
對(duì)于從磁盤(pán)加載的代碼,驗(yàn)證過(guò)程是 JIT 編譯器的一部分,它會(huì)在 JIT 編譯器中間歇性地進(jìn)行。驗(yàn)證和 JIT 編譯不是作為兩個(gè)獨(dú)立的進(jìn)程來(lái)執(zhí)行的。如果驗(yàn)證期間在程序集中找到了一連串不可驗(yàn)證的 MSIL,那么安全系統(tǒng)就會(huì)檢查該程序是否足夠受信任,可以跳過(guò)驗(yàn)證。例如,如果一個(gè)程序集是在安全模型的默認(rèn)設(shè)置下從本地硬盤(pán)上加載的,那可能就是這樣的情況。如果程序集受信任可跳過(guò)驗(yàn)證,MSIL 則會(huì)翻譯成本機(jī)代碼。如果程序集的受信任程度不夠,不可跳過(guò)驗(yàn)證,則會(huì)用一個(gè)存根來(lái)代替有問(wèn)題的 MSIL,如果使用了該執(zhí)行路徑,該存根就會(huì)引發(fā)一個(gè)異常。一個(gè)常見(jiàn)的問(wèn)題是:“為什么不在驗(yàn)證程序集之前檢查它是否需要驗(yàn)證呢?”因?yàn)轵?yàn)證通常是作為 JIT 編譯的一部分執(zhí)行的,所以它通常比檢查是否允許程序集跳過(guò)驗(yàn)證更快。(決定跳過(guò)驗(yàn)證比這里描述的過(guò)程更加智能。例如,可以緩存前面一些驗(yàn)證嘗試的結(jié)果,以提供快速的查找方案。)
除了 MSIL 驗(yàn)證之外,還要驗(yàn)證程序集元數(shù)據(jù)。事實(shí)上,類型安全依賴于這些元數(shù)據(jù)檢查,因?yàn)樗俣?MSIL 驗(yàn)證期間使用的元數(shù)據(jù)標(biāo)記是正確的。在全局程序集緩存 (GAC) 或下載緩存中加載程序集時(shí),會(huì)驗(yàn)證程序集元數(shù)據(jù);如果沒(méi)有將它插入到 GAC 中,則從磁盤(pán)中讀取它時(shí)也會(huì)驗(yàn)證程序集元數(shù)據(jù)。(GAC 是由一些程序使用的程序集的中央存儲(chǔ)。下載緩存保存了從其他位置(如 Internet)下載的程序集。)元數(shù)據(jù)驗(yàn)證包括檢查元數(shù)據(jù)標(biāo)記和消除緩沖區(qū)溢出,前者用于檢查它們是否會(huì)正確索引到它們?cè)L問(wèn)的表中,以及到字符串表的索引是否并不指向長(zhǎng)度大于應(yīng)該保存它們的緩沖區(qū)大小的字符串。通過(guò) MSIL 驗(yàn)證和元數(shù)據(jù)驗(yàn)證消除非類型安全的類型安全代碼是運(yùn)行庫(kù)上安全性的第一部分。
代碼訪問(wèn)安全性
從本質(zhì)上講,代碼訪問(wèn)安全性根據(jù)程序集證據(jù)向程序集分配權(quán)限。在決定代碼對(duì)哪些資源應(yīng)該有訪問(wèn)權(quán)限時(shí),代碼訪問(wèn)安全性會(huì)使用可執(zhí)行代碼從中取得的位置和有關(guān)代碼標(biāo)識(shí)的其他信息作為一個(gè)主要因素。有關(guān)程序集標(biāo)識(shí)的信息稱為 證據(jù)。一旦將程序集加載到運(yùn)行庫(kù)用于執(zhí)行時(shí),宿主環(huán)境就會(huì)向程序集附加一些證據(jù)。運(yùn)行庫(kù)中的代碼訪問(wèn)安全系統(tǒng)負(fù)責(zé)將這些證據(jù)映射到一個(gè)權(quán)限集,該權(quán)限集又將決定此代碼對(duì)一些資源(比如注冊(cè)表或文件系統(tǒng))具有什么訪問(wèn)權(quán)限。這種映射是以可管理的安全策略為基礎(chǔ)的。
對(duì)于托管代碼的大多數(shù)應(yīng)用程序方案,所設(shè)計(jì)的默認(rèn)代碼訪問(wèn)安全性策略是安全而且是足夠的。它嚴(yán)格地限制了來(lái)自不完全受信任或不受信任環(huán)境(如 Internet 或本地 Intranet)的何種代碼在本地計(jì)算機(jī)上執(zhí)行時(shí)能夠運(yùn)行。因此代碼訪問(wèn)安全性默認(rèn)策略模型代表著通往安全的一種可行途徑。默認(rèn)情況下資源是安全的;管理員需要采取顯式的操作才能使得系統(tǒng)安全性稍差一些。
我們?yōu)槭裁催€需要另一種安全方案呢?與用戶標(biāo)識(shí)相對(duì),代碼訪問(wèn)安全性是以代碼標(biāo)識(shí)為中心的。這使得代碼可以在一個(gè)用戶上下文中,以無(wú)限數(shù)量的信任級(jí)別運(yùn)行。例如,即使運(yùn)行其中的操作系統(tǒng)用戶上下文允許完全訪問(wèn)所有系統(tǒng)資源,來(lái)自 Internet 的代碼也只能在限定的安全邊界中運(yùn)行。
現(xiàn)在讓我們來(lái)看一下代碼訪問(wèn)安全系統(tǒng)的主要輸入和輸出:證據(jù)和權(quán)限。
權(quán)限
權(quán)限代表著可執(zhí)行受保護(hù)操作的授權(quán)。這些操作通常包括對(duì)特定資源的訪問(wèn)。通常,這些操作可以包括訪問(wèn)資源,如文件、注冊(cè)表、網(wǎng)絡(luò)、用戶界面或執(zhí)行環(huán)境等。不涉及實(shí)際資源的權(quán)限的一個(gè)示例是跳過(guò)驗(yàn)證功能。
注
System.Security.Permissions.SecurityPermission 類包含一個(gè)標(biāo)志,該標(biāo)志確定是否允許權(quán)限實(shí)例的接收者跳過(guò)驗(yàn)證。SecurityPermission 類包含了其他類似的權(quán)限標(biāo)志,它們涵蓋了核心的運(yùn)行庫(kù)技術(shù),如果不正確使用這些技術(shù)(如控制為在特定應(yīng)用程序域中運(yùn)行的程序集提供的證據(jù)的能力),就可能公開(kāi)安全漏洞。核心運(yùn)行庫(kù)技術(shù)是由請(qǐng)求調(diào)用方來(lái)保護(hù)的,以便使得必需的 SecurityPermission 類設(shè)置合適的權(quán)限標(biāo)志。
權(quán)限的基本抽象是 IPermission 接口,它要求特定的權(quán)限類型來(lái)實(shí)現(xiàn)一組標(biāo)準(zhǔn)的權(quán)限操作,如返回與具有相同權(quán)限類型的其他權(quán)限實(shí)例的聯(lián)合或子集。
可以將權(quán)限整理到一個(gè)權(quán)限集中,該權(quán)限集代表著對(duì)各種資源的訪問(wèn)權(quán)限的一種聲明。 System.Security.PermissionSet 類代表著權(quán)限的集合。這個(gè)類上的方法包括 Intersect 和 Union。這些方法采用另一個(gè) PermissionSet 作為參數(shù),并提供了一個(gè) PermissionSet,該P(yáng)ermissionSet要么是這兩個(gè)集合中所有權(quán)限的聯(lián)合,要么是這兩個(gè)集合中所有權(quán)限的交集。(運(yùn)行庫(kù)中的權(quán)限集合是以一個(gè)簡(jiǎn)單的、未排序的集合表示的。)有了這些工具,安全系統(tǒng)就可以使用權(quán)限集,而不必理解每種權(quán)限類型的語(yǔ)義了。這使得開(kāi)發(fā)人員可以擴(kuò)展權(quán)限的層次結(jié)構(gòu),而無(wú)需修改安全引摯的功能。
注 每種權(quán)限類型必須派生自要求任何權(quán)限類型來(lái)實(shí)現(xiàn)標(biāo)準(zhǔn)權(quán)限操作的 IPermission 接口,這些標(biāo)準(zhǔn)權(quán)限操作如聯(lián)合、交集、子集和請(qǐng)求方法。這些權(quán)限類型不用實(shí)現(xiàn)特定于它們包含的權(quán)限狀態(tài)類型的語(yǔ)義。例如,一個(gè)包含文件名的權(quán)限將與一個(gè)包含簡(jiǎn)單布爾值狀態(tài)的權(quán)限相交而產(chǎn)生不同結(jié)果。當(dāng)權(quán)限集 A 與權(quán)限集 B 相交時(shí),如果 A 和 B 包含相同權(quán)限類型 X 的不同實(shí)例,那么權(quán)限集類 A 就會(huì)調(diào)用 X 的實(shí)例上的交集方法,而不必知道有關(guān) X 的語(yǔ)義的任何內(nèi)容。
根據(jù)在程序集加載時(shí)提供給安全系統(tǒng)的證據(jù),安全系統(tǒng)將授予一個(gè)權(quán)限集,該權(quán)限集代表著訪問(wèn)各種受保護(hù)資源的權(quán)限。相反,資源是由權(quán)限請(qǐng)求來(lái)保護(hù)的,該請(qǐng)求會(huì)觸發(fā)一個(gè)安全檢查,以查看是否將一個(gè)特定的權(quán)限授予了該資源的所有調(diào)用方;如果請(qǐng)求失敗,就會(huì)引發(fā)一個(gè)異常。(有一個(gè)稱為鏈接請(qǐng)求的特定安全檢查,它只檢查直接調(diào)用方。但通常會(huì)檢查調(diào)用方的整個(gè)調(diào)用堆棧。)
證據(jù)
無(wú)論何時(shí)向運(yùn)行庫(kù)中加載程序集時(shí),宿主環(huán)境都會(huì)向安全系統(tǒng)提供該程序集的證據(jù)。證據(jù)構(gòu)成了向代碼訪問(wèn)安全性策略系統(tǒng)輸入的內(nèi)容,這些輸入決定了程序集會(huì)收到哪些權(quán)限。
.NET 框架附帶了一些類,在安全系統(tǒng)中,這些類是以證據(jù)的標(biāo)準(zhǔn)形式使用的:
? |
區(qū)域:與 Internet 中使用的區(qū)域具有相同的概念。
|
? |
URL
:一個(gè)標(biāo)識(shí)特定資源的特定 URL 文件位置,如 http://www.microsoft.com/test。
|
? |
散列:使用像 SHA1 這樣的散列算法生成的程序集散列值。
|
? |
強(qiáng)名稱:程序集的強(qiáng)名稱簽名。強(qiáng)名稱代表著一種版本化、加密的加強(qiáng)方式,用于引用和標(biāo)識(shí)特定簽名方的一個(gè)(或全部)程序集。有關(guān)詳細(xì)信息,請(qǐng)參閱 .NET Framework SDK。
|
? |
站點(diǎn):代碼來(lái)自的站點(diǎn)。URL 比站點(diǎn)的概念更具體;例如 www.microsoft.com 就是一個(gè)站點(diǎn)。
|
? |
應(yīng)用程序目錄:要從中加載代碼的目錄。
|
? |
發(fā)行者證書(shū):程序集的 Authenticode 數(shù)字簽名。
|
注 從理論上講,任何托管對(duì)象都可以構(gòu)成證據(jù)。上面只是一些在 .NET 框架中具有對(duì)應(yīng)成員條件的類型,因此可以將它們集成到安全策略中,而不必編寫(xiě)自定義安全對(duì)象。有關(guān)安全策略和代碼組的詳細(xì)信息,請(qǐng)參閱下面的內(nèi)容。
下面的程序是證據(jù)的一個(gè)簡(jiǎn)單示例,其中證據(jù)是在加載程序集時(shí)被傳遞到運(yùn)行庫(kù)安全系統(tǒng)的。本例中,mscorlib 是加載的程序集,它是包含了許多運(yùn)行庫(kù)類型(如 Object 和 String)的程序集。
using System;
using System.Collections;
using System.Reflection;
using System.Security.Policy;
namespace AssemblyEvidence
{
class Class1
{
static void Main(string[] args)
{
Type t = Type.GetType("System.String");
Assembly a = Assembly.GetAssembly(t);
Evidence e = a.Evidence;
IEnumerator i = e.GetEnumerator();
while(i.MoveNext())
Console.WriteLine(i.Current);
}
}
}
程序的輸出顯示了對(duì)于此程序集向安全系統(tǒng)傳遞了哪些證據(jù)。為簡(jiǎn)潔起見(jiàn),已對(duì)下面的輸出進(jìn)行了編輯。安全系統(tǒng)采用這個(gè)證據(jù),然后根據(jù)管理員設(shè)置的安全策略為該程序集生成了一個(gè)權(quán)限集。
<System.Security.Policy.Zone version="1">
<Zone>MyComputer</Zone>
</System.Security.Policy.Zone>
<System.Security.Policy.Url version="1">
<Url>
file:///C:/winnt/microsoft.net/framework/v1.0.2728/mscorlib.dll
</Url>
</System.Security.Policy.Url>
<StrongName version="1"
Key="00000000000000000400000000000000"
Name="mscorlib"
Version="1.0.2411.0"/>
<System.Security.Policy.Hash version="1">
<RawData>4D5A90000300000004000000FFFF0000B8000000000000...
0000000000000000000000000000000000000000000000000000
</RawData>
</System.Security.Policy.Hash>
安全策略
可管理的安全策略決定了宿主環(huán)境提供給程序集的證據(jù)和授予該程序集的權(quán)限集之間的映射。System.Security.SecurityManager 類實(shí)現(xiàn)了這種映射功能。 因此,您可以將代碼訪問(wèn)安全性策略系統(tǒng)看作一個(gè)帶有兩個(gè)輸入變量(證據(jù)和可管理的安全策略)的函數(shù),可以將程序集特定的權(quán)限集看作輸出值。本節(jié)重點(diǎn)講述可管理的安全策略系統(tǒng)。
有一些安全管理器可以識(shí)別的可配置策略級(jí)別,它們是:
? |
企業(yè)策略級(jí)別
|
? |
計(jì)算機(jī)策略級(jí)別
|
? |
用戶策略級(jí)別
|
? |
應(yīng)用程序域策略級(jí)別
|
企業(yè)策略級(jí)別、計(jì)算機(jī)策略級(jí)別和用戶策略級(jí)別可由安全策略管理員進(jìn)行配置。應(yīng)用程序域策略級(jí)別可以通過(guò)宿主以編程的方式來(lái)配置。
當(dāng)安全管理器需要決定安全策略授予程序集的權(quán)限集時(shí),它是從企業(yè)策略級(jí)別開(kāi)始的。將程序集證據(jù)提供給這個(gè)策略級(jí)別將會(huì)從該策略級(jí)別授予權(quán)限集。通常,安全管理器會(huì)以相同的方式繼續(xù)收集企業(yè)策略級(jí)別以下策略級(jí)別的權(quán)限集。然后這些權(quán)限集會(huì)進(jìn)行相交,以生成該程序集的策略系統(tǒng)權(quán)限集。所有策略級(jí)別都必須首先允許一個(gè)特定的權(quán)限,然后才能使其進(jìn)入為該程序集授予的權(quán)限集中。例如,如果在程序集的計(jì)算期間,企業(yè)策略級(jí)別沒(méi)有授予一個(gè)特定的權(quán)限,那么不管其他級(jí)別指定了什么權(quán)限,也不會(huì)授予權(quán)限。
注 有一些特殊的情況,某個(gè)策略級(jí)別中(如企業(yè)策略級(jí)別)可能包含了一條指令,該指令指定不計(jì)算該級(jí)別之下的任何策略級(jí)別,如計(jì)算機(jī)策略級(jí)別和用戶策略級(jí)別。在該種情況下,計(jì)算機(jī)策略級(jí)別和用戶策略級(jí)別都不會(huì)生成一個(gè)權(quán)限集,并且在授予程序集權(quán)限集的計(jì)算中不會(huì)考慮這兩個(gè)級(jí)別。
程序集的開(kāi)發(fā)人員可以影響程序集運(yùn)行庫(kù)進(jìn)行的權(quán)限計(jì)算。盡管程序集不能簡(jiǎn)單地取得運(yùn)行所需的權(quán)限,但它可以聲明一個(gè)最低限度需要的權(quán)限集或拒絕某些權(quán)限。安全管理器會(huì)確保只有需要的一個(gè)(或多個(gè))權(quán)限是策略級(jí)別結(jié)構(gòu)授予的權(quán)限集的一部分時(shí),程序集才會(huì)運(yùn)行。相反,安全管理器還確保程序集不會(huì)收到它拒絕獲取的任何權(quán)限。程序集的開(kāi)發(fā)人員可以通過(guò)使用安全自定義屬性將最低限度需要權(quán)限、拒絕權(quán)限或可選權(quán)限放入程序集中。有關(guān)詳細(xì)信息,請(qǐng)參閱下面 聲明性方式和命令性方式 部分或 .NET Framework SDK。
決定授予程序集一個(gè)實(shí)際權(quán)限集的過(guò)程包括三個(gè)步驟:
1.各個(gè)策略級(jí)別計(jì)算程序集的證據(jù),然后生成特定于策略級(jí)別的授予權(quán)限集。
2.為每個(gè)策略級(jí)別計(jì)算的權(quán)限集彼此進(jìn)行相交。
3.得到的權(quán)限集與程序集聲明的運(yùn)行所需的權(quán)限集或拒絕權(quán)限進(jìn)行比較,然后相應(yīng)地修改權(quán)限的授予。
圖
1.
一個(gè)程序集的授予權(quán)限集的計(jì)算
圖 1 顯示了大概的計(jì)算過(guò)程。運(yùn)行庫(kù)的宿主提供有關(guān)程序集的證據(jù),該證據(jù)作為程序集收到權(quán)限集的計(jì)算的一個(gè)輸入??晒芾淼陌踩呗裕ㄆ髽I(yè)策略、計(jì)算機(jī)策略和用戶策略)是決定程序集的權(quán)限集計(jì)算的第二個(gè)輸入,本文前面稱之為安全性設(shè)置。然后安全策略代碼(包含在 SecurityManager 類中)遍歷各個(gè)策略級(jí)別設(shè)置提供的程序集證據(jù),然后生成一個(gè)權(quán)限集,它代表了該程序集可訪問(wèn)受保護(hù)資源的權(quán)限集合。
每個(gè)策略級(jí)別是如何管理的呢?策略級(jí)別表示一個(gè)獨(dú)立、可配置的安全策略單元 – 每個(gè)級(jí)別都將程序集證據(jù)映射到一個(gè)權(quán)限集。每個(gè)策略級(jí)別都具有類似的結(jié)構(gòu)。每個(gè)策略級(jí)別都由三個(gè)部分組成,這三個(gè)部分組合在一起用來(lái)表示策略級(jí)別的配置狀態(tài):
? |
代碼組樹(shù)
|
? |
命名權(quán)限列表
|
? |
策略程序集列表
|
現(xiàn)在我們來(lái)詳細(xì)闡述所有策略級(jí)別的這些組成部分。
代碼組
每個(gè)策略級(jí)別的核心是代碼組樹(shù)。它表示策略級(jí)別的配置狀態(tài)。代碼組實(shí)質(zhì)是一個(gè)條件表達(dá)式和一個(gè)權(quán)限集。如果程序集滿足該條件表達(dá)式,那么就會(huì)被授予該權(quán)限集。每個(gè)策略級(jí)別的代碼組集是按樹(shù)的形式組織的。一旦條件表達(dá)式的計(jì)算結(jié)果為真,就會(huì)授予該權(quán)限集,然后繼續(xù)在那個(gè)分支中遍歷。只要不滿足條件,就不授予權(quán)限集,也就不再進(jìn)一步檢查那個(gè)分支。例如,有一個(gè)代碼組樹(shù),它是按照下面的這種情況運(yùn)行的。
圖
2.
策略級(jí)別的代碼組樹(shù)
注 這里我們只討論用于實(shí)現(xiàn)默認(rèn)安全策略的代碼組類型的代碼組語(yǔ)義。也可以包括自定義編寫(xiě)的、語(yǔ)義完全不同于這里所描述語(yǔ)義的代碼組。再提一下,安全系統(tǒng)是完全可以擴(kuò)展的,因此它為引入新的策略計(jì)算語(yǔ)義提供了無(wú)限的可能性。
假設(shè)有一個(gè)程序集,它具有下列證據(jù):它來(lái)自 www.monash.edu.au,因?yàn)樗鼇?lái)自 Monash University 的 m-Commerce Center,所以它的強(qiáng)名稱為 mCommerce。
代碼組樹(shù)遍歷則會(huì)按照下列方式進(jìn)行:
根節(jié)點(diǎn)有一個(gè)任何代碼都滿足的條件 "all code"。因此將 "all code" 權(quán)限授予了我們的這個(gè)程序集,這個(gè)權(quán)限集稱為 ”Nothing”,它不允許代碼具有任何權(quán)限。下一個(gè)檢查的代碼組是要求從 My Computer 加載代碼的代碼組。因?yàn)檫@個(gè)條件不滿足,所以未授予權(quán)限集,因此也不會(huì)檢查這個(gè)條件的任何下級(jí)節(jié)點(diǎn)。然后我們返回上一個(gè)成功授權(quán)的代碼組(本例中是 all code),然后繼續(xù)檢查它的下級(jí)節(jié)點(diǎn)。下一個(gè)代碼組是 Zone:Internet。因?yàn)槲覀兊拇a是從 Internet 上下載的,所以滿足這個(gè)條件,從而授予了權(quán)限集(可能是 Internet 權(quán)限集),然后您就可以繼續(xù)檢查這個(gè)分支中的下一個(gè)下級(jí)代碼組了。下一個(gè)代碼組有一個(gè) Url: 條件:指出代碼來(lái)自 www.microsoft.com。由于代碼是來(lái)自 www.monash.edu.au 的,所以不滿足這個(gè)條件。此時(shí)我們返回到 Zone:Internet 代碼組,查找它下面的其他節(jié)點(diǎn)。我們?yōu)?URL:www.monash.edu.au 查找節(jié)點(diǎn)。由于滿足了這個(gè)條件,我們得到了 MonashPSet 權(quán)限集。接下來(lái)我們?yōu)?Strong Name:m-Commerce center 查找節(jié)點(diǎn)。由于滿足這個(gè)條件,我們得到了 m-Commerce 權(quán)限集。因?yàn)檫@個(gè)級(jí)別下面沒(méi)有代碼組,所以我們返回到匹配條件并且具有下級(jí)代碼組的上一個(gè)代碼組,然后繼續(xù)。
最終,滿足的條件和從這個(gè)策略級(jí)別中授予的權(quán)限集包括:
? |
條件:All code,權(quán)限集:Nothing
|
? |
條件:Zone:Internet,權(quán)限集:Internet
|
? |
條件:URL:www.monash.edu.au,權(quán)限集:MonashPSet
|
? |
條件:Strong Name:m-Commerce,權(quán)限集:m-CommercePSet
|
在一個(gè)策略級(jí)別找到的適用于某個(gè)特定程序集的所有權(quán)限集通常會(huì)進(jìn)行聯(lián)合,以生成該策略級(jí)別授予的總權(quán)限集。
檢查一個(gè)策略級(jí)別的代碼組樹(shù)是非常簡(jiǎn)單的。附錄 A 描述了一個(gè) Microsoft 管理控制臺(tái)單元,該單元提供了一個(gè)用于查看和修改代碼組的層次結(jié)構(gòu)(以及策略級(jí)別的其他所有可配置組成部分,請(qǐng)參閱下面的內(nèi)容)的可視界面。
命名的權(quán)限集
一個(gè)策略級(jí)別包含一個(gè)命令權(quán)限集列表。每個(gè)權(quán)限集代表著一個(gè)信任聲明,用于訪問(wèn)各種受保護(hù)資源。命名的權(quán)限集是代碼組按其名稱進(jìn)行引用的一些權(quán)限集。如果滿足了代碼組的條件,那么就授予被引用的命名權(quán)限集(請(qǐng)參閱上面的示例)。下面是一些預(yù)定義的命名權(quán)限集示例:
? |
FullTrust
:允許不受限制地訪問(wèn)系統(tǒng)資源。
|
? |
SkipVerification
:可以跳過(guò)驗(yàn)證的程序集。
|
? |
Execution
:允許代碼執(zhí)行。
|
? |
Nothing
:不授予權(quán)限。不授予執(zhí)行的權(quán)限可有效停止代碼的運(yùn)行。
|
? |
Internet
: 適合來(lái)自 Internet 的代碼的權(quán)限集。代碼將不能收到對(duì)于文件系統(tǒng)或注冊(cè)表的訪問(wèn)權(quán)限,但可以執(zhí)行一些有限的用戶界面操作,并且可以使用稱為獨(dú)立存儲(chǔ)的安全文件系統(tǒng)。
|
為了查看策略級(jí)別的權(quán)限集,只要在附錄 A 提及的 GUI 工具中打開(kāi)策略級(jí)別節(jié)點(diǎn),然后打開(kāi)權(quán)限集文件夾即可。
下面是一個(gè)很小的示例程序,它列出了所有策略級(jí)別上所有已知的命名權(quán)限集:
下面的程序顯示了所有策略級(jí)別上的命名權(quán)限集列表。該應(yīng)用程序是一個(gè)從本地磁盤(pán)上運(yùn)行的 C# 程序,因此它會(huì)從默認(rèn)的策略設(shè)置中收到一個(gè)相當(dāng)強(qiáng)大的權(quán)限集。
using System;
using System.Collections;
using System.Security;
using System.Security.Policy;
namespace SecurityResolver
{
class Sample
{
static void Main(string[] args)
{
IEnumerator i = SecurityManager.PolicyHierarchy();
while(i.MoveNext())
{
PolicyLevel p = (PolicyLevel) i.Current;
Console.WriteLine(p.Label);
IEnumerator np = p.NamedPermissionSets.GetEnumerator();
while (np.MoveNext())
{
NamedPermissionSet pset = (NamedPermissionSet)np.Current;
Console.WriteLine("\tPermission Set: \n\t\t Name: {0}
\n\t\t Description {1}",
pset.Name, pset.Description);
}
}
}
}
}
該程序的輸出。為簡(jiǎn)潔和明確起見(jiàn),已對(duì)該輸出進(jìn)行了編輯。
Enterprise
Permission Set:
Name: FullTrust
Description: Allows full access to all resources
Permission Set:
Name: LocalIntranet
Description: Default rights given to applications
on your local intranet
...
Machine
Permission Set:
Name: Nothing
Description: Denies all resources, including the right to execute
...
User
...
Name: SkipVerification
Description: Grants right to bypass the verification
Permission Set:
Name: Execution
Description: Permits execution
...
策略程序集
在安全計(jì)算期間,可能需要加載其他的程序集以在策略計(jì)算過(guò)程中使用。例如,一個(gè)程序集可以包含代碼組發(fā)出權(quán)限集的用戶定義權(quán)限類部分。當(dāng)然也需要計(jì)算這個(gè)包含自定義權(quán)限的程序集。如果這個(gè)自定義權(quán)限的程序集被授予包含它自己實(shí)現(xiàn)的自定義權(quán)限的權(quán)限集,那么就會(huì)引發(fā)循環(huán)依賴項(xiàng)。為了避免這種情況,每個(gè)策略級(jí)別都包含了一個(gè)它在策略計(jì)算中所需的受信任程序集列表。這個(gè)必需程序集的列表自然地被稱為了“策略程序集”列表,它包含了在這個(gè)策略級(jí)別實(shí)現(xiàn)安全策略所需的所有程序集的過(guò)渡閉包。包含在該列表中的所有程序集的策略計(jì)算是短路的,以避免發(fā)生循環(huán)依賴項(xiàng)。該列表可以使用附錄 A 提及的GUI 管理工具來(lái)修改。
這就完成了每個(gè)策略級(jí)別上的可配置組成部分的檢查:代碼組樹(shù)、命名權(quán)限集列表和策略程序集列表。現(xiàn)在該來(lái)看一下從安全策略狀態(tài)派生的授予權(quán)限集是如何在由每個(gè)策略級(jí)別的配置實(shí)例化時(shí),與安全強(qiáng)制的基礎(chǔ)結(jié)構(gòu)進(jìn)行連接的。換言之,迄今為止,我們只討論了程序集是如何收到授予的權(quán)限集的。如果沒(méi)有一個(gè)基礎(chǔ)結(jié)構(gòu)要求程序集具有某一級(jí)別的權(quán)限,那么安全系統(tǒng)將不能發(fā)揮任何作用。事實(shí)上,使安全強(qiáng)制成為可能的技術(shù)是安全堆棧審核。
堆棧審核
堆棧審核是安全系統(tǒng)的重要組成部分。堆棧審核以如下方式進(jìn)行操作。每次調(diào)用方法時(shí),都會(huì)在堆棧中放入一個(gè)新的激活記錄。這個(gè)記錄包含了要傳遞給該方法的參數(shù)(如果有參數(shù)的話)、此函數(shù)結(jié)束時(shí)要返回的地址以及任何局部變量。當(dāng)程序執(zhí)行時(shí),堆棧就會(huì)隨著函數(shù)的調(diào)用而增長(zhǎng)和收縮。在執(zhí)行中的某些階段,該線程可能需要訪問(wèn)系統(tǒng)資源,如文件系統(tǒng)。在允許這種對(duì)受保護(hù)資源的訪問(wèn)之前,可能需要進(jìn)行一次堆棧審核來(lái)驗(yàn)證調(diào)用鏈中的所有方法是否具有訪問(wèn)系統(tǒng)資源的權(quán)限。在這一階段會(huì)進(jìn)行堆棧審核,并且通常會(huì)檢查每個(gè)激活記錄,以查看這些調(diào)用方是否確實(shí)具有所需的權(quán)限。與完全堆棧審核相比,CAS 系統(tǒng)還允許開(kāi)發(fā)人員使用鏈接時(shí)間檢查來(lái)批注資源,這種鏈接時(shí)間檢查只檢查直接調(diào)用方。
修改堆棧審核
在執(zhí)行期間的任何階段,在某個(gè)函數(shù)訪問(wèn)特定資源之前,它都可能需要檢查其調(diào)用方的權(quán)限。在這一階段,該函數(shù)可以要求對(duì)一個(gè)特定的權(quán)限或權(quán)限集進(jìn)行安全檢查。這就會(huì)觸發(fā)堆棧審核,其結(jié)果是:如果所有的調(diào)用方都具有授予的權(quán)限,那么該函數(shù)就繼續(xù)執(zhí)行;如果調(diào)用方不具有所需的一個(gè)(或多個(gè))權(quán)限,就會(huì)引發(fā)異常。下圖說(shuō)明了這個(gè)過(guò)程。
圖
3.
堆棧審核示例
函數(shù)可以選擇修改堆棧審核,有一些機(jī)制可完成這種修改。首先,一個(gè)函數(shù)可能需要保證調(diào)用它的多個(gè)函數(shù)。在這種情形下,它可以斷言一個(gè)特定的權(quán)限。如果發(fā)生了查找斷言的權(quán)限的堆棧審核,那么在檢查這個(gè)函數(shù)的激活記錄尋找該權(quán)限時(shí),如果該函數(shù)具有它斷言的權(quán)限,則該檢查將成功,堆棧審核也會(huì)終止。斷言本身是一個(gè)受保護(hù)操作,因?yàn)樗鼘⑾驍嘌栽L問(wèn)受保護(hù)資源權(quán)限的函數(shù)的所有調(diào)用方開(kāi)放對(duì)該受保護(hù)資源的訪問(wèn)權(quán)限。因此,在運(yùn)行庫(kù)中,安全系統(tǒng)會(huì)檢查包含自我斷言的函數(shù)的程序集是否具有它試圖斷言的權(quán)限。
修改堆棧審核的另一種辦法是支持函數(shù)拒絕權(quán)限。當(dāng)一個(gè)函數(shù)知道它不應(yīng)該訪問(wèn)某個(gè)資源并拒絕權(quán)限時(shí),就可能發(fā)生這種情形。PermitOnly 提供了類似 deny 的功能,因?yàn)樗鼤?huì)導(dǎo)致堆棧審核失敗。但 deny 指定的是會(huì)導(dǎo)致堆棧審核失敗的權(quán)限集,而 PermitOnly 指定的則是繼續(xù)堆棧審核所需的權(quán)限集。
注 當(dāng)使用 Deny 堆棧修飾符時(shí),您應(yīng)該非常謹(jǐn)慎。如果早期的堆棧幀是一個(gè)斷言,則會(huì)忽略 Deny 修飾符。另外,拒絕基于路徑的權(quán)限是相當(dāng)困難的,這是因?yàn)榻?jīng)常有各種不同的路徑字符串實(shí)際是指向相同位置的。拒絕一個(gè)特定路徑表達(dá)式仍會(huì)開(kāi)放其他的路徑。
還有最后一個(gè)需要知道的要點(diǎn)。在任何時(shí)刻,一個(gè)堆棧幀只能有一個(gè) Deny,一個(gè) PermitOnly 和一個(gè) Assert 處于有效狀態(tài)。例如,如果開(kāi)發(fā)人員需要斷言許多權(quán)限,他們就應(yīng)該創(chuàng)建一個(gè) PermissionSet 來(lái)表示該集合,并只進(jìn)行一個(gè)單獨(dú)的斷言。有一些方法可用來(lái)刪除一個(gè)堆棧審核修飾符的當(dāng)前 PermissionSet 設(shè)置,以便注冊(cè)其他的權(quán)限集。這樣方法的一個(gè)示例是 System.Security.codeAccessPermission.RevertPermitOnly。
下面的示例說(shuō)明了前面介紹的各種堆棧修改技術(shù):
using System;
using System.Security;
using System.Security.Permissions;
namespace PermissionDemand
{
class EntryPoint
{
static void Main(string[] args)
{
String f = @"c:\System Volume Information";
FileIOPermission p =
new FileIOPermission(
FileIOPermissionAccess.Write, f);
p.Demand();
p.Deny();
p.Demand();
CheckDeny(p);
p.Assert();
CheckDeny(p);
}
static void CheckDeny(FileIOPermission p)
{
try
{
p.Demand();
}
catch(SecurityException)
{
Console.WriteLine("Demand failed");
}
}
}
}
前面的程序產(chǎn)生了下面的輸出,這些輸出起先看起來(lái)很不直觀:
Demand failed
Demand failed
上面的代碼示例中,盡管第一個(gè) Demand 訪問(wèn)的是一個(gè)受限制系統(tǒng)目錄,但它還是成功了。記住,運(yùn)行庫(kù)安全系統(tǒng)是在基礎(chǔ)操作系統(tǒng)設(shè)置上面的。因此,可以讓運(yùn)行庫(kù)安全策略對(duì)某些目錄授予這樣的訪問(wèn)權(quán)限,即托管代碼實(shí)際試圖訪問(wèn)這些目錄時(shí),它將會(huì)因?yàn)檫@樣做引發(fā)操作系統(tǒng)訪問(wèn)沖突。Deny 后面的下一個(gè) Demand 也成功了。當(dāng)執(zhí)行 Demand 時(shí),沒(méi)有檢查請(qǐng)求函數(shù)的激活記錄,而只檢查它的調(diào)用方。因此,盡管函數(shù)已經(jīng)拒絕訪問(wèn),但也不會(huì)被 Demand 檢測(cè)到。調(diào)用 CheckDeny 和后面的 Demand 確實(shí)失敗了?,F(xiàn)在檢查前面方法中的 Deny,因?yàn)樗挥谡{(diào)用方的堆棧幀中。接下來(lái)我們返回到 Main 并進(jìn)行一個(gè) Assert。這里已經(jīng)斷言的一個(gè)權(quán)限在這個(gè)堆棧幀中也被拒絕了。我們進(jìn)入 CheckDeny ?±,Demand 再次引發(fā)一個(gè)異常,但這是為什么呢?從本質(zhì)上講,Deny 重寫(xiě)了 Assert;這是因?yàn)?Deny 權(quán)限集總是在 Assert 權(quán)限集之前進(jìn)行檢查。
概括起來(lái),使托管資源可以引發(fā)托管安全堆棧審核的功能是保護(hù)資源的運(yùn)行庫(kù)安全系統(tǒng)方法。授予的權(quán)限集是程序集從每個(gè)策略級(jí)別上運(yùn)行的授權(quán)計(jì)算收到的,該權(quán)限集會(huì)與資源請(qǐng)求的權(quán)限進(jìn)行對(duì)照檢查。如果后者形成了前者的一個(gè)子集,那就可以訪問(wèn)受保護(hù)資源。除非已像上面描述的那樣對(duì)堆棧進(jìn)行了修改,否則就會(huì)對(duì)調(diào)用鏈中托管資源的所有調(diào)用方執(zhí)行這種子集檢查。因此安全堆棧審核綜合了運(yùn)行庫(kù)安全系統(tǒng)的這兩個(gè)方面:1} 據(jù)和權(quán)限之間的可配置映射;2) 通過(guò)強(qiáng)制所有的調(diào)用方擁有一定級(jí)別的權(quán)限來(lái)保護(hù)資源。
實(shí)際上有兩種不同的方式可用于以編程的方式表達(dá)堆棧審核請(qǐng)求和堆棧修改操作 — 聲明性安全和命令性安全。
聲明性方式和命令性方式
.NET 框架允許開(kāi)發(fā)人員以兩種方式表達(dá)安全約束。以 聲明性方式 表達(dá)安全約束意味著使用自定義屬性語(yǔ)法。這些批注保留在類型的元數(shù)據(jù)中,在編譯時(shí)會(huì)有效地溶入到程序集中。下面是聲明性方式安全的一個(gè)示例:
[PrincipalPermissionAttribute(SecurityAction.Demand,
Name=@"culex\damien")]
以命令性方式表達(dá)安全要求意味著在運(yùn)行時(shí)創(chuàng)建 Permission 對(duì)象的實(shí)例并調(diào)用它們的方法。下面是本文前面示例中聲明性方式安全的一個(gè)示例:
FileIOPermission p = new FileIOPermission(
FileIOPermissionAccess.Write, f);
p.Demand();
為什么選擇一種方式而不選擇另一種方式的原因有幾個(gè)。首先,可以使用聲明性方式來(lái)表達(dá)所有的安全操作;但對(duì)于命令性方式,則不行。不過(guò),聲明性方式要求在編譯時(shí)表達(dá)所有安全約束,而且只允許在完全方法、類或程序集范圍內(nèi)存在批注。命令性方式更加靈活,因?yàn)樗试S在運(yùn)行時(shí)表達(dá)約束,如對(duì)于方法中可能執(zhí)行路徑的子集。將命令性安全要求保留在元數(shù)據(jù)中的一個(gè)副作用是,有一些工具可提取這些元數(shù)據(jù)信息并根據(jù)這些信息提供功能。例如,某個(gè)工具可以顯示程序集上聲明性安全屬性集的列表。而對(duì)于命令性安全,這種情況是不可能發(fā)生的。開(kāi)發(fā)人員需要了解并熟悉這兩種方式。
忠告
因?yàn)榘凑漳J(rèn)策略,從本地硬盤(pán)上執(zhí)行的代碼將會(huì)明顯比從其他任何位置執(zhí)行的代碼取得更多的信任,所以下載代碼、將代碼儲(chǔ)存到磁盤(pán)然后執(zhí)行它與從遠(yuǎn)程位置執(zhí)行代碼具有完全不同的語(yǔ)義。對(duì)于以前的系統(tǒng),這種情況不是很明顯,例如瀏覽器選擇的是下載代碼而不是遠(yuǎn)程執(zhí)行它。這里假定在代碼下載后,執(zhí)行前還會(huì)對(duì)它進(jìn)行檢查,比如病毒掃描。使用了代碼訪問(wèn)安全性,情況就完全不同了。如果使用默認(rèn)的安全策略,則將代碼作為遠(yuǎn)程代碼執(zhí)行會(huì)明顯增加安全性。然而這時(shí)可能會(huì)增加系統(tǒng)或用戶的負(fù)擔(dān),因?yàn)樗麄冃枰劳泄艽a和非托管代碼的區(qū)別。最后是有關(guān)運(yùn)行庫(kù)安全系統(tǒng)的另一方面。對(duì)于舊式安全系統(tǒng)的用戶來(lái)說(shuō),他們應(yīng)該更熟悉這個(gè)系統(tǒng),因?yàn)樗且杂脩魳?biāo)識(shí)為基礎(chǔ)的:基于角色的安全性。
基于角色的安全性
迄今為止介紹的代碼訪問(wèn)安全系統(tǒng),基本上是以代碼標(biāo)識(shí)為中心的,而不是以用戶或角色為中心的。然而仍需要根據(jù)用戶標(biāo)識(shí)來(lái)表達(dá)安全設(shè)置。因此運(yùn)行庫(kù)安全系統(tǒng)也附帶了基于角色的安全性特性。
基于角色的安全性利用了用戶和角色的概念,這與目前許多操作系統(tǒng)中的安全實(shí)現(xiàn)相類似。基于角色的安全性中的兩個(gè)核心抽象是 Identity 和 Principal。 Identity 是代碼執(zhí)行時(shí)所代表的用戶。請(qǐng)謹(jǐn)記,它們可能是應(yīng)用程序或開(kāi)發(fā)人員定義的邏輯用戶,而不必是操作系統(tǒng)可以看到的用戶。 Principal 代表了用戶和用戶所屬角色的抽象。代表用戶標(biāo)識(shí)的類實(shí)現(xiàn) Identity 接口。在 .Net 框架中,提供這個(gè)接口的默認(rèn)實(shí)現(xiàn)的一般類是 GenericIdentity。代表 Principal 的類實(shí)現(xiàn) IPrincipal 接口。在 .Net 框架中,提供這個(gè)接口的默認(rèn)實(shí)現(xiàn)的一般類是 GenericPrincipal。
在運(yùn)行庫(kù),每個(gè)線程都有且只有一個(gè)當(dāng)前的 Principal 對(duì)象與之相關(guān)聯(lián)。當(dāng)然代碼可以根據(jù)安全要求來(lái)訪問(wèn)和更改這個(gè)對(duì)象。每個(gè) Principal 都有且只有一個(gè) Identity 對(duì)象。從邏輯上講,對(duì)象的運(yùn)行庫(kù)結(jié)構(gòu)與下面的內(nèi)容相類似:
圖
4.
基于角色安全性結(jié)構(gòu)
下面的程序說(shuō)明了開(kāi)發(fā)人員可以如何使用這些一般類。本例中,開(kāi)發(fā)人員正在提供安全模型。例如,名稱 “Damien” 和角色 “Lecturer“、“Examiner“ 與操作系統(tǒng)可以支持的任何用戶和角色都無(wú)關(guān)。
using System;
using System.Threading;
using System.Security;
using System.Security.Principal;
namespace RoleBasedSecurity
{
class Sample
{
static void Main(string[] args)
{
String [] roles = {"Lecturer", "Examiner"};
GenericIdentity i = new GenericIdentity("Damien");
GenericPrincipal g = new GenericPrincipal(i,
roles);
Thread.CurrentPrincipal = g;
if(Thread.CurrentPrincipal.Identity.Name ==
"Damien")
Console.WriteLine("Hello Damien");
if(Thread.CurrentPrincipal.IsInRole("Examiner"))
Console.WriteLine("Hello Examiner");
if(Thread.CurrentPrincipal.IsInRole("Employee"))
Console.WriteLine("Hello Employee");
}
}
}
程序產(chǎn)生了如下的輸出:
Hello Damien
Hello Examiner
如果開(kāi)發(fā)人員希望的話,也可以使用 Microsoft (R) Windows (R) 安全模型。在這種情形中,用戶和角色會(huì)與宿主計(jì)算機(jī)中的用戶和角色緊密相連,因此可能需要在宿主系統(tǒng)上創(chuàng)建這些帳戶。下面的示例使用的是本地計(jì)算機(jī)上的用戶帳戶。本例中也使用了一些 syntactic sugar(語(yǔ)法糖塊),.NET 框架中的 PrincipalPermissionAttribute 類有效地封裝了一些方法的調(diào)用(如 IsInRole),以使得開(kāi)發(fā)人員可以使用簡(jiǎn)化的語(yǔ)法。
namespace RoleBased
{
class Sample
{
[PrincipalPermissionAttribute(SecurityAction.Demand,
Name=@"culex\damien")]
public static void UserDemandDamien()
{
Console.WriteLine("Hello Damien!");
}
[PrincipalPermissionAttribute(SecurityAction.Demand,
Name=@"culex\dean")]
public static void UserDemandDean()
{
Console.WriteLine("Hello Dean!");
}
static void Main(string[] args)
{
AppDomain.CurrentDomain.SetPrincipalPolicy(
PrincipalPolicy.WindowsPrincipal);
try
{
UserDemandDamien();
UserDemandDean();
}
catch(Exception)
{
Console.WriteLine("Exception thrown");
}
}
}
}
PrincipalPermissionAtribute 保證了每次調(diào)用 UserDemandDamien 和 UserDemandDean 方法時(shí)都會(huì)進(jìn)行運(yùn)行庫(kù)檢查。當(dāng)然,程序可能由 Dean、Damien 也可能由其他人執(zhí)行,因此對(duì)這兩個(gè)方法調(diào)用的安全檢查即使未全部失敗,也至少有一個(gè)會(huì)失敗。Main 的第一行將用戶策略設(shè)置成了 Windows(執(zhí)行本例的操作系統(tǒng))的用戶策略。當(dāng)用戶 ”culex\damien” 執(zhí)行程序時(shí)將產(chǎn)生如下輸出:
Hello Damien!
Exception thrown
小結(jié)
安全性是 .NET 框架的一個(gè)基本和內(nèi)置的因素。本文旨在對(duì)安全系統(tǒng)進(jìn)行一下概述??梢詮谋疚目偨Y(jié)出來(lái)的一些主要概念有:
? |
安全系統(tǒng)是可擴(kuò)展的,因?yàn)樵?.NET 框架中,很多概念都是以類型表示的,因此開(kāi)發(fā)人員可根據(jù)他們自己的需要進(jìn)行擴(kuò)展和修改。
|
? |
安全系統(tǒng)提供了不同類型的安全模型,具體地說(shuō),有基于角色的安全模型和基于證據(jù)的安全模型。這些不同的模型滿足了不同的需要,并且是相互補(bǔ)充的。
|
? |
代碼訪問(wèn)安全性是以代碼的標(biāo)識(shí)為中心的,因此即使代碼運(yùn)行的操作系統(tǒng)用戶上下文授予了它對(duì)計(jì)算機(jī)的管理權(quán)限,也只允許在不完全受信任的安全上下文中執(zhí)行該代碼。
|
本文沒(méi)有論及像加密這些方面的安全系統(tǒng)問(wèn)題,也沒(méi)對(duì)任何方面進(jìn)行非常詳細(xì)的論述。請(qǐng)參閱白皮書(shū),特別是有關(guān)這些主題的內(nèi)容,以了解本文沒(méi)有討論的一些細(xì)節(jié)。
致謝
在編寫(xiě)本文的過(guò)程中,Brian Pratt、Loren Kohnfelder 和 Matt Lyons 提供了大力的幫助和支持,謹(jǐn)在此表示感謝。
附錄 A:MsCorCfg.msc
有一個(gè) Microsoft 管理控制臺(tái)單元使我們能夠可視化地操縱代碼訪問(wèn)安全性策略。下圖顯示了這個(gè)管理單元的界面,其中突出顯示了本文論及的一些概念。
圖
5. Microsoft
管理控制臺(tái)單元界面
您可以在 Control Panel 中單擊 Administrative Tools,然后單擊 Microsoft .NET Framework Configuration 快捷鍵,來(lái)訪問(wèn)該工具。
http://wayfarer.cnblogs.com/articles/48022.html