MSDN 首頁 

程式碼存取安全性實務

發佈日期: 2004 年 5 月 28 日
本頁內容
本單元內容本單元內容
目標目標
適用於適用於
如何使用本單元如何使用本單元
解說的程式碼存取安全性解說的程式碼存取安全性
APTCAAPTCA
特殊權限程式碼特殊權限程式碼
要求權限要求權限
授權程式碼授權程式碼
連結要求連結要求
Assert 與 RevertAssertAssert 與 RevertAssert
限制程式碼限制程式碼
檔案 I/O檔案 I/O
事件日誌事件日誌
登錄登錄
資料存取資料存取
目錄服務目錄服務
環境變數環境變數
Web 服務Web 服務
通訊端與 DNS通訊端與 DNS
Unmanaged 程式碼Unmanaged 程式碼
委派委派
序列化序列化
總結總結
其他資源其他資源

本單元內容

程式碼存取安全性是一種資源限制模型,設計上是要限制程式碼能夠存取的系統資源,以及程式碼能夠執行的特殊權限操作類型。這些限制和呼叫程式碼的使用者,或和程式碼執行的使用者帳戶都無關。本單元開始會先詳細說明程式碼存取安全性的運作,接著提供齊全的範例,涵蓋能夠宣告、需求、要求或允許程式碼存取安全性權限的所有資源。

本單元提供您對程式碼存取安全性內部工作的充份認證,包括原則評估、原則層級、APTCA、特殊權限程式碼、使用權限要求、程式授權、連結要求及執行。

本單元會介紹程式碼存取安全性的三個主要優點,並示範如何使用程式碼存取安全性來:

限制程式碼能做的事
例如,您若是開發一個執行檔案 I/O 的組件,可以使用程式碼存取安全性限制程式碼對特定檔案或目錄的存取權限。這會減少攻擊者強制您的程式碼存取自訂檔案的機會。

限制哪些程式碼可以呼叫您的程式碼
例如,您可能只希望讓您的組織開發的其他程式碼呼叫您的組件。執行方式之一,是使用組件的增強名稱的公開金鑰元件套用這種限制。這有助於防止惡意程式碼呼叫您的程式碼。

辨識程式碼
若要成功地管理程式碼存取安全性原則及限制程式碼能夠執行的操作,程式碼必須能夠辨識。程式碼存取安全性使用舉證 (例如組件的增強名稱或 URL) 或其處理的雜湊,來辨識程式碼 (組件)。

回到頁首回到頁首

目標

透過本單元即可:

瞭解程式碼存取安全性如何運作,以及何謂特殊權限程式碼。

使用程式碼存取安全性,限制必須執行檔案 I/O、資料存取、存取事件日誌、登錄、目錄服務、環境變數、Web 服務、通訊端及 DNS 的程式。

安全且有效地套用 APTCA。

要求權限和授權程式碼。

沙箱 (Sandbox) 管理特殊權限程式碼。

知道何時使用連結要求,何時不使用。

知道何時執行權限要求,何時不執行。

有效地使用權限要求和要求。

使用程式碼存取安全性,進一步增強呼叫 Unmanaged 程式碼、使用序列化及使用委派的安全性。

回到頁首回到頁首

適用於

本單元適用於下列產品及技術:

Microsoft Windows Server 2000 及 2003

Microsoft .NET Framework 1.1

ASP.NET 1.1

回到頁首回到頁首

如何使用本單元

本單元內容緊接單元 7<建置安全的組件>。單元中將介紹如何使用程式碼存取安全性,進一步增強 Managed 程式碼的安全性。若要充分瞭解本單元:

閱讀單元 6<.NET 安全性基礎>裡的概觀,以及使用者或角色為基礎的安全性與程式碼存取安全性的比較。單元 6 提供本單元的背景。

閱讀單元 7<建置安全的組件>。如果尚未閱讀單元 7,請先閱讀之後再閱讀本單元。

閱讀單元 9<配合 ASP.NET 使用程式碼存取安全性>。閱讀過本單元後,如果對 ASP.NET 程式碼存取安全性原則與 ASP.NET 信任等級特別有興趣,請閱讀單元 9。

回到頁首回到頁首

解說的程式碼存取安全性

若要有效地使用程式碼存取安全性,必須知道術語及如何評估原則等基礎。如需程式碼存取安全性的更多背景資訊,請參閱本單元後面的「其他資源」一節。如果您已經很熟悉程式碼存取安全性,請略過這一節,直接閱讀本單元後面的「APTCA」(AllowPartiallyTrustedCallersAttribute) 一節。

程式碼存取安全性包含下列項目:

程式碼

舉證

權限

原則

程式碼群組

程式碼

所有 Managed 程式碼都受到程式碼存取安全性管制。載入組件時,會授與其一組程式碼存取權限,這組權限決定它能夠存取的資源類型,以及可以執行之特殊權限操作的類型。Microsoft .NET Framework 安全性系統使用舉證驗證 (識別) 程式碼授與使用權限。

注意 組件是程式碼存取安全性的設定與信任的單位。同一組件內所有程式碼會收到相同的權限授與,因此都同樣地得到信任。

舉證

.NET Framework 安全性系統使用舉證來辨識組件。程式碼存取安全性原則使用舉證,協助將正確的權限授與正確的組件。位置相關的舉證包括:

URL:取得組件的 URL。這是簡略格式的基礎碼 URL,例如 http://webserver/vdir/bin/assembly.dll 或 file://C:/directory1/directory2/assembly.dll。

網站:取得組件的來源網站,例如 http://webserver。網站是從基礎碼 URL 衍生。

應用程式目錄:執行應用程式的基礎目錄。

區域:取得組件的來源區域,例如 LocalIntranet 或網際網路。區域也是從基礎碼 URL 取得。

作者相關的舉證包括:

增強名稱:這適用於使用增強名稱的組件。增強名稱是使用私密金鑰為組件數位簽章的一種方式。

發行者:Authenticode 簽章;以簽署程式碼所使用的 X.509 憑證為基礎,代表開發組織。

重要 ASP.NET 主機會忽略發行者舉證 (Authenticode 簽章),因此這種舉證不能用於設定伺服器端 Web 應用程式的程式碼存取安全性原則。這種舉證主要是由 Internet Explorer 主機使用。

雜湊:組件雜湊是以組件的整體內容為基礎,能夠讓您偵測一段程式碼的特定編譯,不受版本編號影響。這在偵測協力廠商組件變更 (沒有更新版本編號),而您尚未測試及授權其在您建置中的使用時,非常有用。

權限

權限代表程式碼存取安全資源或執行特殊權限操作的權利。.NET Framework 提供程式碼存取權限 程式碼識別身份權限。程式碼存取權限會封裝存取特定資源或執行特殊權限操作的能力。程式碼識別身份權限用於根據呼叫程式碼的識別身份的某一方面 (例如其增強名稱),限制對程式碼的存取。

您的程式碼由系統管理員所設定的程式碼存取安全性原則來授與權限。組件也可以使用權限要求,影響最終授與的權限集合。程式碼存取安全性原則和權限要求一起使用,決定了您的程式碼能做的事。例如,程式碼必須得到授與 FileIOPermission,才能存取檔案系統,而必須得到授與 RegistryPermission,才能存取登錄。如需權限要求的詳細資訊,請參閱本單元後面的「要求權限」一節。

注意 權限集合用於將權限歸組在一起,以便管理。

限制與無限制權限

權限可以有限制 無限制。例如,在無限制狀態下,FileIOPermission 允許程式碼讀取或寫入檔案系統的任一部分。而在限制狀態下,可能只允許程式碼從特定的目錄讀取檔案。

要求

如果您使用 .NET Framework 類別庫裡的類別來存取資源或執行其他特殊權限操作,類別會發出權限要求,確保您的程式碼,以及呼叫您程式碼的任何程式碼都得到存取資源的授權。權限要求會造成執行時期 (Run Time) 往回執行呼叫堆疊查核行程 (一個個堆疊框架),檢查堆疊中每個呼叫者的權限。如果發現任何呼叫者沒有必要的權限,會擲回 SecurityException

連結要求

連結要求不會執行完整的堆疊查核行程 (Stack Walk),只會檢查呼叫堆疊前一個堆疊框架的呼叫者。因此,使用連結要求會有額外的安全性風險。您必須特別注意偽裝攻擊。

注意 偽裝攻擊的惡意程式會透過信任的中介組件呼叫您的程式碼,存取您的組件公開的資源和操作。

如需如何正確使用連結要求的詳細資訊,請參閱本單元後面的「連結要求」一節。

Assert、Deny 及 PermitOnly 方法

程式碼存取權限類別支援 AssertDenyPermitOnly 方法。您可以使用這些方法改變權限要求堆疊查核行程的行為。這些稱為堆疊查核行程修飾元

呼叫 Assert 方法會造成符合權限的堆疊查核行程在 Assert 呼叫的站台停止。這最常用於沙箱管理特殊權限程式碼。如需詳細資訊,請參閱本單元後面的「Assert 與 RevertAssert」一節。

呼叫 Deny 方法會捨棄已傳遞且包含符合權限的任何堆疊查核行程。如果呼叫不信任的程式碼,可以使用 Deny 方法,限制所呼叫之程式碼的功能。

呼叫 PermitOnly 方法會捨棄任何不符合的堆疊查核行程。它和 Deny 方法一樣很少使用,但是可用於限制您會呼叫之一些不信任程式碼的動作。

原則

程式碼存取安全性原則是由系統管理員設定,決定了授與組件的權限。原則可以在四個層級建立:

企業:用於套用整個企業的原則。

電腦:用於套用電腦層級的原則。

使用者:用於套用每個使用者原則。

應用程式領域:用於設定組件所載入的目標應用程式領域。

ASP.NET 實作應用程式領域原則,可以讓您設定 Web 應用程式與 Web 服務的程式碼存取安全性原則。如需 ASP.NET 應用程式領域原則的詳細資訊,請參閱單元 9<配合 ASP.NET 使用程式碼存取安全性>。

原則設定是在 XML 設定檔案中設定。前三個原則層級 (企業、電腦及使用者) 都可以使用 .NET Framework 組態工具設定,這個工具位於 [管理工具] 程式群組或 Caspol.exe 指令行公用程式中。ASP.NET 應用程式領域層級原則目前必須使用文字或 XML 編輯器來編輯。

如需原則檔與位置的詳細資訊,請參閱單元 19<保障 ASP.NET 應用程式及 Web 服務的安全>。

程式碼群組

每個原則檔包含一個程式碼群組的階層集合。程式碼群組用於指派權限給組件。程式碼群組包含兩個元素:

成員資格條件:以舉證為基礎,例如,組件的區域或其增強名稱。

權限集合:權限集合中包含的權限會授與舉證符合成員資格條件的組件。

運作方式

[圖 8.1] 顯示程式碼存取安全性的簡要概觀。

程式碼存取安全性 - 簡化檢視

[圖 8.1]
程式碼存取安全性 – 簡化檢視

[圖 8.1] 裡顯示的步驟總結如下:

1.

載入組件。

這項操作是由應用程式領域主機執行。在載入 Web 應用程式組件的 Web 伺服器上,這是 ASP.NET 主機。

2.

舉證是從組件收集,由主機表示。

3.

舉證是針對定義的安全性原則評估。

4.

安全性原則評估的輸出是一或多個具名權限集合,定義了授與組件的權限。

注意 組件可以包含權限要求,要求則可進一步減少權限授與。

5.

組件中的程式碼會在存取限制資源或執行特殊權限操作之前,要求適當的權限。

存取資源或執行特殊權限操作的所有 .NET Framework 基底類別都包含適當的權限要求。例如,FileStream 類別會要求 FileIOPermissionRegistry 類別會要求 RegistryPermission 等。

6.

如果組件 (及其呼叫者) 已獲授與要求的權限,操作就可以繼續。否則會產生安全性例外。

評估原則的方式

透過原則引擎執行舉證時,其輸出是一個權限集合,定義了授與組件的權限集合。原則授與是在原則階層的每一層級計算:企業、電腦、使用者及應用程式領域。然後再使用交集作業結合每個層級產生的原則授與,產生最後的原則授與。使用交集確保階層中較低層級的原則,不能加入較高層級所未授與的權限。這可以防止個別使用者或應用程式領域,授與企業系統管理員未授與的其他權限。

[圖 8.2] 顯示交集作業如何表示產生的權限授與,是由原則階層中所有的原則層級決定。

跨越各原則層級的原則交集

[圖 8.2]
跨越各原則層級的原則交集

從 [圖 8.2] 可以看出,交集作業確保只有每個層級授與的權限會構成最後權限授與的一部分。

權限要求如何影響原則授與?

您可以在組件加入安全性屬性,以指定其權限需求。您可以指定組件若要執行,必須得到授與的最小權限集合。這些不會影響權限授與。您也可以指定組件可以使用,但並不絕對需要的選用性權限,以及要拒絕的權限。拒絕的權限是您要保證即使安全性原則有授與,您的組件還是絕不會擁有的組件。

如果您要求選用的權限,結合的選用與最小權限會與原則授與交集,使授與更為減少。然後,再從原則授與剔除明確拒絕的權限。這可以利用以下公式來總結,其中 PG 系統管理員定義之安全性原則的原則授與,而 PminPoptPrefused 是開發者加入組件的權限要求。

產生的權限授與 = (PG ∩ (Pmin ∪ Popt)) - Prefused

如需如何使用權限要求、其含意及使用時機的詳細資訊,請參閱本單元後面的「要求權限」一節。

原則層級的原則評估

每個特定層級的個別原則檔包含程式碼群組的階層排列。這些程式碼群組包含用於決定套用群組之目標組件的成員資格條件,以及用於決定應授與符合組件之權限的權限集合。階層式結構允許將多個權限集合指派給一個組件,也允許安全性原則支援簡單的 AND 和 OR 邏輯。例如,請看 [圖 8.3] 所示的範例安全性原則。

單一原則層級的階層式程式碼群組

[圖 8.3]
單一原則層級的階層式程式碼群組

注意 All Code 程式碼群組是符合所有組件的特殊程式碼群組。它形成安全性原則的根,本身並未授與權限,因為它和名為 Nothing 的權限集合關聯。

請根據 [圖 8.3] 所示的安全性原則授與權限。

My_Computer_Zone (安裝在本機的任何組件) 產生的任何組件都會得到授與 FullTrust 權限集合定義的權限。這是安裝 .NET Framework 時定義的內建權限集合,代表無限制的所有權限集合。

由 Company1 撰寫,從內部網路區域產生的組件會獲得授與內建 LocalIntranet_Zone 權限集合和 Comp1PSet 權限集合定義的權限。

由 Company2 撰寫的組件,會得到授與自訂 Comp2PSet 權限集合定義的權限。

從 a.b.c.com 下載的任何組件會得到自訂 ABCPSet 權限集合定義的權限。

注意 如果特定程式碼群組的成員資格條件未得到滿足,將不會評估其任何子項。

專有與最終層級程式碼群組

原則階層處理與周遊可以使用在程式碼群組層級指定的許多屬性微調,兩者都可以利用 .NET Framework 組態工具設定。分別是:

專有
表示不應將其他同層程式碼群組與這個程式碼群組結合。要將程式碼標記為專有,請從 .NET Framework 組態工具中選取 [這個原則層級只會具有這個程式碼群組使用權限集合的使用權限]

最終層級
表示應忽略任何較低的層級原則。若要將程式碼群組標記為最終層級,請在 .NET Framework 組態工具中選取 [這個層級以下的原則層級不會被評估]。例如,電腦原則中的符合程式碼群組若標記為最終層級,將會忽略使用者原則檔中的原則設定。

注意 不論最終層級設定為何,一律會評估應用程式領域層級原則,例如伺服器端 Web 應用程式的 ASP.NET 原則。

回到頁首回到頁首

APTCA

除非增強名稱組件如下所示包含 AllowPartiallyTrustedCallersAttribute (APTCA),否則部分信任組件 (未授與完全信任的組件) 不能呼叫有增強名稱的組件:

[assembly: AllowPartiallyTrustedCallersAttribute()]

這是為了確保您的程式碼不致因疏忽而向部分信任 (可能是惡意的) 程式碼公開所設計的降低風險策略。Common Language Runtime 會悄悄地將 FullTrust 權限集合的連結要求加入增強名稱組件中所有可公開存取的成員類型。如果包含 APTCA,就會抑制此一連結要求。

避免使用 APTCA

如果使用 APTCA,您的程式碼會立即更容易遭到攻擊,因此特別要檢查您程式碼裡的安全性弱點。只有在完全需要時才使用 APTCA。

在伺服器端 Web 應用程式中,如果您的組件必須支援部分信任呼叫者,就使用 APTCA。這種情形可能發生在下列情況下:

您的組件將由部分信任 Web 應用程式呼叫。也就是 <trust> 層級設定在 Full 以外之內容的應用程式。如需部分信任 Web 應用程式及在此情形下使用 APTCA 的詳細資訊,請參閱單元 9<配合 ASP.NET 使用程式碼存取安全性>。

您的組件將被由程式碼存取安全性管理員授與有限權限的其他組件呼叫。

您的組件將由使用 SecurityAction.RequestRefuse SecurityAction.RequestOptional 拒絕特定權限的其他組件呼叫。這使呼叫組件成為部分信任組件。

您的組件將由使用堆疊查核行程修飾元 (例如 Deny PermitOnly) 限制下游程式碼的其他組件呼叫。

診斷 APTCA 的問題

如果您想要呼叫部分信任程式碼 (例如部分信任 Web 應用程式) 中未標示 APTCA 的增強名稱組件,會看到如 [圖 8.4] 中所示的例外。請注意,例外狀況詳細資訊並未提供權限的詳細資料,只會表示無法從呼叫組件取得要求的權限 (在此情況下是 FullTrust)。在上述情況中,有些令人混淆的描述文字表示因為應用程式的 <trust> 層級設定為 Full 以外的內容,所以發生錯誤。

部分信任程式碼呼叫增強名稱組件的結果

[圖 8.4]
部分信任程式碼呼叫增強名稱組件的結果

為了去除這種例外狀況,必須授與呼叫程式碼 FullTrust ,或以 APTCA 註釋被呼叫的組件。請注意,組件中標示了 APTCA 的個別類型還是會要求完全信任呼叫者,因為它們包含了明確的連結要求或完全信任的固定要求,如下列範例中所示。

[PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
[PermissionSet(SecurityAction.Demand, Unrestricted=true)]
回到頁首回到頁首

特殊權限程式碼

您設計及建立安全組件時,必須能夠辨識特殊權限程式碼。這對程式碼存取安全性有很重要的意義。特殊權限程式碼是會存取安全資源或執行其他有安全性顧慮之操作的 Managed 程式碼,例如呼叫 Unmanaged 程式碼、使用序列化或使用反射。特殊權限程式碼之所以有特殊權限,是因為程式碼存取安全性必須先授與其特定的權限,它才能動作。

特殊權限資源

您的程式碼需要特定程式碼存取安全性權限的特殊權限資源如 [表 8.1] 所示。

[表 8.1] 安全資源與相關的權限

安全資源需要權限

資料存取

SqlClientPermission
OleDbPermission
OraclePermission
注意 ADO.NET OLE DB 與 Oracle 管理的提供者目前需要完全信任。

目錄服務

DirectoryServicesPermission

DNS 資料庫

DnsPermission

事件日誌

EventLogPermission

環境變數

EnvironmentPermission

檔案系統

FileIOPermission

隔離儲存區

IsolatedStoragePermission

訊息佇列

MessageQueuePermission

效能計數器

PerformanceCounterPermission

印表機

PrinterPermission

登錄

RegistryPermission

通訊端

SocketPermission

Web 服務 (和其他 HTTP Internet 資源)

WebPermission

特殊權限操作

[表 8.2] 顯示特殊權限操作及呼叫程式碼需要的相關權限。

[表 8.2] 特殊權限操作與相關的權限

操作需要權限

建立及控制應用程式領域

SecurityPermission
SecurityPermissionFlag.ControlAppDomain

指定原則應用程式領域

SecurityPermission
SecurityPermissionFlag.ControlDomainPolicy

宣告安全性權限

SecurityPermission
SecurityPermissionFlag.Assertion

建立及操作舉證

SecurityPermission
SecurityPermissionFlag.ControlEvidence

建立及操作主體物件

SecurityPermission
SecurityPermissionFlag.ControlPrincipal

設定類型與通道遠端

SecurityPermission
SecurityPermissionFlag.RemotingConfiguration

操作安全性原則

SecurityPermission
SecurityPermissionFlag.ControlPolicy

序列化

SecurityPermission
SecurityPermissionFlag.SerializationFormatter

執行緒作業

SecurityPermission
SecurityPermissionFlag.ControlThread

反射

ReflectionPermission

呼叫 Unmanaged 程式碼

SecurityPermission
SecurityPermissionFlag.UnmanagedCode

回到頁首回到頁首

要求權限

您設計及開發組件時,請建立您的程式碼存取之所有資源,以及程式碼執行之所有特殊權限作業的清單。部署時,系統管理員可能需要此一資訊,才能正確設定程式碼存取安全性原則,以及診斷安全性相關的問題。

溝通您的程式碼的權限需求的最佳方法,是使用組件層級宣告式安全性屬性指定最小權限需求。這些屬性通常放在 Assemblyinfo.cs 或 Assemblyinfo.vb 中。這可以讓系統管理員或組件的消費者使用 Permview.exe 工具檢查它需要哪些權限。

RequestMinimum

您可以使用 SecurityAction.RequestMinimum 方法配合宣告式權限屬性,指定組件執行所需的最小權限集合。例如,您的組件若必須存取登錄,但是只需要從特定的機碼抓取設定資料,請使用類似以下的屬性:

[assembly: RegistryPermissionAttribute(
SecurityAction.RequestMinimum, 
Read=@"HKEY_LOCAL_MACHINE\SOFTWARE\YourApp")]

如果您一開始就知道程式碼會在完全信任環境中執行,而且會得到完整的不受限制權限集合,則使用 RequestMinimum 就較不重要。但是,指定組件的權限需求是一個良好的習慣。

注意 權限屬性接受強制性建構函式引數之後以逗號隔開的屬性與屬性值清單。這些用於將基礎權限物件初始化。快速找出哪些屬性名稱有支援的方法,是在包含權限屬性類型的組件上使用 Ildasm.exe。

RequestOptional

若您使用 SecurityAction.RequestOptional 方法,除了以 SecurityAction.RequestMinimum SecurityAction.RequestOptional 指定的權限以外,不會指定其他權限給您的組件,即使程式碼存取安全性原則已另外授與其他權限也一樣。

RequestRefused

SecurityAction.RequestRefuse 可以讓您確保程式碼存取安全性原則無法授與您的組件並未要求的權限。例如,您的組件若未呼叫 Unmaged 程式碼,可以使用以下屬性確保程式碼存取安全性原則不會授與您的組件 Unmanaged 程式碼權限。

[assembly: SecurityPermissionAttribute(SecurityAction.RequestRefuse, 
UnmanagedCode=true)]

使用 RequestOptional 或 RequestRefuse 的含意

若您使用 RequestOptional,以 RequestOptional RequestMinimum 指定的權限集合會和原則給予您組件的權限授與交集。這表示 RequestOptional RequestMinimum 集合之外的其他所有權限,都會從組件的權限授與移除。此外,您若使用 RequestRefuse,也會從您的組件的權限授與移除拒絕的權限。

因此,您若使用 RequestOptional RequestRefuse,您的組件會變成部分信任組件,在您呼叫其他組件時有其含意。使用下列注意事項,幫助您決定是否應使用 SecurityAction.RequestOptional SecurityAction.RequestRefuse

如果需要直接呼叫增強名稱組件,不使用 AllowPartiallyTrustedCallersAttribute (APTCA),請不要使用,因為這會讓您無法呼叫組件。

許多增強名稱 .NET Framework 組件都包含不支援部分信任呼叫者也不包含 APTCA 的類型。如需詳細資訊,以及支援部分信任呼叫者的組件清單,請參閱單元 9<配合 ASP.NET 使用程式碼存取安全性>。

如果您必須呼叫沒有 APTCA 的增強名稱組件,請讓安裝您程式碼的系統管理員知道,程式碼存取安全性原則必須授與完全信任才能正常運作。

如果不需要存取任何 APTCA 組件,請加入權限要求,拒絕您已知自己的組件並不需要的權限。及早測試程式碼,確定您確實不需要這些權限。

如果下游程式碼需要您已拒絕的權限,則在您和下游程式碼之間需有一個方法來宣告權限。否則當堆疊查核行程執行到您的程式碼時,將會產生一個 SecurityException

回到頁首回到頁首

授權程式碼

程式碼存取安全性可以讓您授權呼叫您組件的程式碼。這會減少惡意程式成功呼叫您程式的風險。例如,您可以使用識別身份權限,根據識別舉證 (例如其增強名稱的公開金鑰元件) 限制呼叫程式碼。您也可以使用明確的程式碼存取權限要求,確保呼叫您組件的程式碼具有必要的權限,能夠存取您的組件公開的資源或執行特殊權限操作。

通常不需要明確地要求程式碼存取權限。.NET Framework 類別會幫您做,沒有必要重複要求。但是,有些時候您還是要發出明確的要求,例如,您的程式碼使用 Unmanaged 程式碼公開自訂資源,或程式碼存取快取資料。您可以使用下列方式授權程式碼:

限制哪個程式碼可以呼叫您的程式碼

限制繼承

考慮保護快取資料

以自訂權限保護自訂資源

限制哪個程式碼可以呼叫您的程式碼

標示為 public 的方法可以被目前組件之外的任何程式碼呼叫。若要進一步限制能夠呼叫您的方法的其他程式碼,可以使用程式碼存取安全性識別身份權限要求,如以下範例所示。

public sealed class Utility
{
// 雖然 SomeOperation() 是公用方法,不過以下
// 權限要求表示只有具有指定之公開金鑰的組件
// 能夠呼叫它。 
[StrongNameIdentityPermission(SecurityAction.LinkDemand, 
PublicKey="00240000048...97e85d098615")]
public static void SomeOperation() {}
}

上面的程式碼顯示連結要求。這會產生直接呼叫者的授權。因此,您的程式碼很可能對偽裝攻擊開放,惡意組件有可能透過具有指定增強名稱的信任中介組件,存取您的組件所提供的保護資源或操作。

根據您的類別所提供之功能的性質,除了使用識別身份為基礎的連結要求之外,可能還需要要求其他權限以授權呼叫程式碼。或者,可以考慮使用完全要求配合 StrongNameIdentityPermission,雖然這會假設呼叫堆疊中所有的程式碼都使用相同的私密金鑰簽署增強名稱。

注意 如果您的組件是由 Web 應用程式或 Web 服務呼叫,發出 StrongNameIdentityPermission 的完全堆疊查核行程要求沒有作用。因為無法為 ASP.NET Web 應用程式或 Web 服務相關之動態編譯類別加上增強名稱。

若要從組件擷取公開金鑰

執行以下命令,從組件取得公開金鑰的十六進位表現:

secutil -hex -strongname yourassembly.dll

若要從金鑰組檔案擷取公開金鑰

1.

以下面的命令產生金鑰組檔案:

sn -k keypairfile

2.

從金鑰組檔案擷取公開金鑰:

sn -p keypairfile publickeyfile

3.

取得公開金鑰的十六進位表現:

sn -tp publickeyfile < publickeyhex.dat

限制繼承

如果您的類別設計為基底類別,您可以使用繼承要求配合 StrongNameIdentityPermission,限制允許其他哪個程式碼從您的類別繼承。這可以防止您的類別,從未使用與要求中之公開金鑰對應之私密金鑰簽署的任何組件繼承。

// 以下繼承要求可確保只有具備指定之公開金鑰
// (組件之增強名稱的一部分) 組件內的程式碼
// 可以使用子類別 SomeRestrictedClass
[StrongNameIdentityPermission(SecurityAction.InheritanceDemand,
PublicKey="00240000048...97e85d098615")]
public class SomeRestrictedClass
{
}

考慮保護快取資料

如果您是使用 .NET Framework 類別之一存取資源,類別會發出適合所考慮之資源類型的權限要求。如果您後來為了效能的原因快取資料,應該考慮在存取快取資料之前,發出明確的程式碼存取權限要求。這可確保呼叫程式碼能得到授權,存取特定類型的資源。例如,您若是從檔案讀取資料然後快取,而您要確保呼叫程式碼得到授權,請發出 FileIOPermission 要求,如以下範例所示。

// 以下要求假設快取資料原來是從
// C:\SomeDir\SomeFile.dat 擷取
new FileIOPermission(FileIOPermissionAccess.Read, 
@"C:\SomeDir\SomeFile.dat").Demand();
// 接著存取快取,並將資料傳回給呼叫者

以自訂權限保護自訂資源

如果您使用 Unmanaged 程式碼公開資料或操作,應該將包裝函式程式碼以沙箱隔離,並考慮要求自訂權限以授權呼叫程式碼。

只要權限類型執行 IUnrestrictedPermission 介面,就會自動授與完全信任呼叫者自訂權限。除非程式碼存取安全性原則明確授與部分信任呼叫者權限,否則部分信任呼叫者不會有權限。這會確保不受信任程式碼無法呼叫您的組件,以存取組件公開的自訂資源。沙箱也表示不會強制您授與需要呼叫您程式碼的任何程式碼強大的 UnmanagedCodePermission

如需呼叫 Unmanaged 程式碼的詳細資訊,請參閱本單元後面的「Unmanaged 程式碼」。如需自訂權限的實作範例,請參閱本指南<How To>一節中的<How To:建立自訂加密權限>。

回到頁首回到頁首

連結要求

連結要求與固定的權限要求不同,差別在於 Run Time 只會從直接呼叫者要求權限,不會執行完全的堆疊查核行程。連結要求是在 JIT 編譯時間執行,只能利用宣告指定。

使用連結要求之前請謹慎考慮,因為使用下去很容易產生安全性弱點。如果使用連結要求,請考慮下列問題:

偽裝攻擊

效能與連結要求

有連結要求的呼叫方法

混合類別與方法層級連結要求

介面與連結要求

結構與連結要求

虛擬方法與連結要求

偽裝攻擊

如果您以連結要求保護程式碼,很容易遭到偽裝攻擊,惡意程式碼會透過信任的中間媒介,取得您的程式碼公開之資源或操作的存取權,如 [圖 8.5] 所示。

利用連結要求進行偽裝攻擊的範例

[圖 8.5]
利用連結要求進行偽裝攻擊的範例

[圖 8.5] 裡,組件 X 裡存取安全資源的方法使用連結保護,會要求特定的公開金鑰 (使用 StrongNameIdentityPermission)。組件 A、B 及 C 是使用與組件 X 信任之公開金鑰對應的私密金鑰簽署,因此這些組件可以呼叫組件 X。如果組件 A、B 和 C 在呼叫組件 X 之前,沒有檢查其呼叫者是否有特定的舉證,就可能受到偽裝攻擊。例如,沒有使用相同私密金鑰簽署的組件 D 不能直接呼叫組件 X。但是,信任組件 A 若沒有以另一個連結要求或透過完全要求檢查呼叫者,組件 D 就可以透過組件 A 存取組件 X。

唯有您信任組件的呼叫者不會再對外公開其功能 (例如,呼叫者是應用程式,而非程式庫時),或您知道以識別權限要求驗證直接呼叫者的識別身份是安全的時,才在組件中使用連結要求。

效能與連結要求

和網路延遲與資料庫存取等其他 Web 應用程式效能問題比較起來,堆疊查核行程的成本很小。不要純綷為了效能的原因來使用連結要求。完全要求提供更高的安全性。

有連結要求的呼叫方法

如果您呼叫連結要求保護方法,連結要求只會檢查您的程式碼。在此情況下,您應該確定程式碼有適當授權呼叫者的方法,例如,要求權限。

混合類別與方法層級連結要求

方法層級連結要求會覆蓋類別層級連結要求。例如,下面的程式碼片段中,FileIOPermission 的連結要求必須在方法宣告上重複,或 EnvironmentPermission 連結要求取代 FileIOPermission 要求的類別層級。

[FileIOPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public sealed class SomeClass
{
// 如果方法有其他連結要求的修飾,不受限制的 FileIOPermission 
// 連結要求必須在方法層級重述。
// 否則 (在本例中) 
// EnvironmentPermission 連結要求將會覆蓋類別層級
// FileIOPermission 連結要求
[FileIOPermission(SecurityAction.LinkDemand, Unrestricted=true)]
[EnvironmentPermission(SecurityAction.LinkDemand, Read="PATH")]
public void SomeMethod()
  {
  }
}

介面與連結要求

如果您的類別實作介面,而方法實作之一有連結要求,請確定介面定義上的方法宣告也有相同的連結要求。否則,呼叫者只需要透過介面呼叫您的方法,避開連結要求。以下是一個範例。

public interface IMyInterface
{
// 以下方法實作上顯示的連結要求 
// 應在此重複
void Method1();
}

public class MyImplementation : IMyInterface
{
// 方法實作有一個連結要求,但是介面沒有
[SecurityPermission(SecurityAction.LinkDemand, 
Flags=SecurityPermissionFlag.ControlPrincipal)]
public void Method1()
  {
  }
}

使用下列程式碼,呼叫者會受到連結要求的管制:

MyImplementation t = new MyImplementation();
t.Method1();

使用下列程式碼,呼叫者不會受到連結要求的管制:

IMyInterface i = new MyImplementation();
i.Method1();

結構與連結要求

連結要求不會防止不受信任的呼叫者建立結構。因為不會自動為結構產生預設的建構函式。所以,唯有在您使用明確的建構函式時,才會套用結構層級連結要求。例如:

[SecurityPermission(SecurityAction.LinkDemand, 
Flags=SecurityPermissionFlag.ControlPrincipal)]
public struct SomeStruct
{
// 這個明確建構函式受到連結要求的保護
public SomeStruct(int i)
  {
field = i;
  }
private int field;
}

下列兩行程式碼都會產生欄位初始化為零的新結構。但是,只有使用明確建構函式的第一行會受到連結要求管制。

SomeStruct s = new SomeStruct(0);
SomeStruct s = new SomeStruct();	

第二行不會受到連結要求管制,因為沒有產生預設的建構函式。如果這是類別,而非建構函式,編譯器會產生以指定之連結要求加註的預設建構函式。

虛擬方法與連結要求

如果您使用連結要求保護衍生類別中的方法覆蓋,請確定也將它放在對應的虛擬基底類別方法中。否則,若是 JIT 編譯器看到沒有連結要求之基底類別類型的參照,將不要執行連結要求。

回到頁首回到頁首

Assert 與 RevertAssert

您可以呼叫 CodeAccessPermission.Assert 方法,以防止要求傳播到目前堆疊框架之外。使用 Assert,可以確認程式碼之呼叫者是否值得信任。因為有潛在的偽裝攻擊風險,所以 Assert 必須小心使用。

Assert 最常用於沙箱管理特殊權限程式碼。如果您開發呼叫 Assert 的程式碼,必須確定有替代的安全性措施可以授權呼叫程式碼。下列建議可以幫助您將風險降到最低。

使用要求 / 宣告模式

減少宣告時間

使用要求 / 宣告模式

在呼叫 Assert 之前要求特定的權限,是授權上游程式碼的有效方法。有時您必須能夠要求內建權限類型,以授權呼叫程式碼。

往往,如果您的組件公開的是 .NET Framework 類別庫未提供的功能,例如呼叫 Data Protection API (DPAPI),您必須開發自訂權限並要求自訂權限授權呼叫者。例如,您可以開發一個自訂 Encryption 權限,授與呼叫者 Managed DPAPI 包裝函式組件的授權。要求這個權限,然後宣告 Unmanaged 程式碼權限,是授權呼叫程式碼的一個有效方法。

如需這種方法和開發自訂權限的詳細資訊,請參閱本指南<How To>一節中的<How To:建立自訂加密權限>。

減少宣告時間

如果您只需要呼叫 Assert 以滿足您的程式碼呼叫之單一下游方法的要求,請將 Assert 緊接放在下游方法呼叫之前。然後立即呼叫 RevertAssert,讓宣告視窗儘量保持最小,並確定您的方法呼叫的任何後續程式碼不會意外成功,因為 Assert 仍然生效。

常見的慣例是將 RevertAssert 的呼叫放在 finally 區塊中,保證一律會呼叫到它,即使是在例外狀況時也一樣。

回到頁首回到頁首

限制程式碼

限制程式碼和建立最小權限程式碼,就像設定使用者或服務帳戶時使用最小權限的原則。限制您的程式碼能夠使用的程式碼存取安全性權限,就會縮小惡意使用程式碼的範圍。

有兩種方式可以限制程式碼,限制它可以存取的資源,以及限制它可以執行的其他特殊權限操作:

使用原則權限授與

使用堆疊查核行程修飾元

使用原則權限授與

您可以設定程式碼存取安全性原則,將限制的權限集合授與特定的組件。這會限制它存取資源或執行其他特殊權限操作的能力。如需詳細資訊,請參閱本指南<How To>一節裡的<How To:設定程式碼存取安全性原則以限制組件>。

使用堆疊查核行程修飾元

您可以使用堆疊查核行程修飾元,確保您呼叫的程式碼只能使用特定的權限。例如,可以使用 SecurityAction.PermitOnly,確定您的方法和呼叫的任何方法只有受限制的權限集合。以下範例適用於限制嚴格的權限集合。程式碼只有執行的權限。它不能存取資源或執行其他特殊權限操作。

[SecurityPermissionAttribute(SecurityAction.PermitOnly, 
Flags=SecurityPermissionFlag.Execution)]
public void SomeMethod()
{	
// 目前的方法和下游只能執行。不能存取資源或
// 執行其他特殊權限操作。
SomeOtherMethod();
}

接下來幾節會示範如何使用程式碼存取安全性限制各種類型的資源存取,包括檔案 I/O、事件日誌、登錄、資料存取、目錄服務、環境變數、Web 服務及通訊端。

回到頁首回到頁首

檔案 I/O

若要能夠執行檔案 I/O,必須由程式碼存取安全性原則授與您的組件 FileIOPermission。如果您的程式碼得到授與不受限制的 FileIOPermission,它可以在 Windows 安全性的控制下,存取檔案系統中任一處的檔案。可以使用限制的 FileIOPermission 限制組件執行檔案 I/O 的能力,例如,指定允許的存取權利 (讀取、讀取/寫入等)。

限制您應用程式範圍內的檔案 I/O

能夠將檔案 I/O 限制在特定的目錄位置,例如您的應用程式的目錄階層,這是一般的要求。

注意 如果您的 Web 應用程式設定成中度信任,會自動將檔案存取限制在應用程式的虛擬目錄階層。如需詳細資訊,請參閱單元 9<配合 ASP.NET 使用程式碼存取安全性>。

將您的應用程式設定成中度信任是限制檔案 I/O 的一種方法,不過這也會限制您的應用程式存取其他資源類型的能力。另外有兩種方式可以限制程式碼的檔案 I/O 功能:

使用 PermitOnly 限制檔案 I/O

設定程式碼存取安全性原則以限制檔案 I/O

使用 PermitOnly 限制檔案 I/O

您可以如以下範例中所示,使用宣告式屬性配合 SecurityAction.PermitOnly,來限制檔案 I/O。

// 只允許程式碼從 c:\YourAppDir 讀取檔案
[FileIOPermission(SecurityAction.PermitOnly, Read=@"c:\YourAppDir\")]
[FileIOPermission(SecurityAction.PermitOnly, PathDiscovery=@"c:\YourAppDir\")]
public static string ReadFile(string filename)
{
// 使用 Path.GetFilePath() 將檔案名稱標準化
// 使用 FileStream.OpenRead 開啟檔案
// 使用 FileStream.Read 存取及傳回資料
}

注意 指定 PathDicovery 存取的第二個屬性是 Path.GetFilePath 函式所需要的,這個函式用於將輸入檔案名稱標準化。

若要避免將您的應用程式目錄階層硬式編碼,可以使用命令式安全性語法,以及使用 HttpContext.Current.Request.MapPath(".") ,在 Runtime 時擷取您的 Web 應用程式的目錄。您必須參照 System.Web 組件,並加入對應的 using 陳述式,如以下範例中所示:

using System.Web;

public static string ReadFile(string filename)
{
string appDir = HttpContext.Current.Request.MapPath(".");
FileIOPermission f = new FileIOPermission(PermissionState.None);
f.SetPathList(FileIOPermissionAccess.Read, appDir);
f.SetPathList(FileIOPermissionAccess.PathDiscovery, appDir);
f.PermitOnly();

// 使用 Path.GetFilePath() 將檔案名稱標準化
// 使用 FileStream.OpenRead 開啟檔案
// 使用 FileStream.Read 存取及傳回資料
}

注意 針對 Windows 應用程式,可以將 MapPath 的呼叫換成 Directory.GetCurrentDirectory 的呼叫,以取得應用程式的目前目錄。

設定程式碼存取安全性原則以限制檔案 I/O

系統管理員也可以設定程式碼存取安全性原則,限制您的程式碼在您應用程式之虛擬目錄階層之外執行檔案 I/O 的能力。

例如,系統管理員可以設定企業或電腦層級程式碼存取安全性原則,授與您的組件限制的 FileIOPermission。您的組件包含增強名稱時這最容易做到,因為系統管理員設定原則時可以使用這個密碼法產生的增強式舉證。至於沒有增強名稱的組件,必須改用其他形式的舉證。如需如何設定程式碼存取安全性,以限制組件之檔案 I/O 功能的詳細資訊,請參閱本指南<How To>一節裡的<How To:設定程式碼存取安全性原則以限制組件>。

如果您的組件是由 Web 應用程式呼叫,較好的方法是設定 ASP.NET (應用程式領域層級) 程式碼存取安全性原則,因為您可以使用代表應用程式虛擬根目錄的 $AppDirUrl$。如需使用 ASP.NET 程式碼存取安全性原則限制檔案 I/O 的詳細資訊,請參閱單元 9<配合 ASP.NET 使用程式碼存取安全性>。

要求 FileIOPermission

為了幫助系統管理員,如果您知道自己的組件在建立時的確切檔案 I/O 要求 (例如您知道目錄名稱),請使用宣告式權限要求宣告您組件的 FileIOPermission 要求。

[assembly: FileIOPermission(SecurityAction.RequestMinimum, Read=@"C:\YourAppDir")]

系統管理員可以使用 permview.exe 看到這個屬性。使用 SecurityAction.RequestMinimum 的另一優點,是組件如果未得到足夠的權限授權,將無法載入。這比 Runtime 安全性例外還要好。

回到頁首回到頁首

事件日誌

若要能夠存取事件日誌,程式碼存取安全性原則必須授與您的組件 EventLogPermission。如果沒有,例如,因為它是在中度信任 Web 應用程式的範圍內執行,則您必須沙箱隔離事件日誌程式碼。如需沙箱隔離事件日誌的存取權,請參閱單元 9<配合 ASP.NET 使用程式碼存取安全性>。

限制事件日誌程式碼

如果您要限制事件日誌包裝函式程式碼的動作 — 可能是其他開發者或開發組織所撰寫的程式碼 — 您可以使用宣告式屬性配合 SecurityAction.PermitOnly,如以下範例所示:

以下屬性保證 WriteToLog 方法及其呼叫的任何方法只能存取本機電腦的事件日誌,不能刪除事件日誌或事件來源。EventLogPermissionAccess.Instrument 不允許這些操作。

[EventLogPermission(SecurityAction.PermitOnly, 
MachineName=".",
PermissionAccess=EventLogPermissionAccess.Instrument)]
public static void WriteToLog( string message )

若要強制對現有日誌的唯讀存取權限,請使用 EventLogPermissionAccess.Browse

要求 EventLogPermission

若要記錄您程式碼的權限要求,並確定程式碼存取安全性原則若未授與足夠的事件日誌存取權限時,無法載入您的組件,請加入組件層級的 EventLogPermissionAttributeSecurityAction.RequestMinimum 內容,如以下範例所示。

// 這個屬性表示您的程式碼只需要存取本機電腦上
// 之事件日誌的能力 ("."),而且需要儀器化存取,
// 表示可以讀取或寫入現有的日誌,以及建立新的
// 事件來源與事件日誌
[assembly: EventLogPermissionAttribute(SecurityAction.RequestMinimum,
MachineName=".",
PermissionAccess=
EventLogPermissionAccess.Instrument)]
回到頁首回到頁首

登錄

使用 Microsoft.Win32.Registry 類別存取登錄的程式碼必須由程式碼存取安全性原則授與 RegistryPermission。這種權限類型可用於將登錄存取限制在特定的機碼和子機碼,也可以控制程式碼讀取、寫入或建立登錄機碼與名稱值的能力。

限制登錄存取

若要限制程式碼讀取特定登錄機碼的資料,可以使用 RegistryPermissionAttribute SecurityAction.PermitOnly。以下屬性保證程式碼只能從 HKEY_LOCAL_MACHINE\SOFTWARE 之下的 YourApp 機碼 (和子機碼) 讀取。

[RegistryPermissionAttribute(SecurityAction.PermitOnly,
Read=@"HKEY_LOCAL_MACHINE\SOFTWARE\YourApp")]
public static string GetConfigurationData( string key, string namedValue )
{
return (string)Registry.
LocalMachine.
OpenSubKey(key).
GetValue(namedValue);
}

要求 RegistryPermission

若要記錄程式碼的權限要求,並確定程式碼存取安全性原則若未授與足夠的登錄存取權限時,無法載入您的組件,請如以下範例所示加入組件層級 RegistryPermissionAttribute 和內容 SecurityAction.RequestMinimum

[assembly: RegistryPermissionAttribute(SecurityAction.RequestMinimum,
Read=@"HKEY_LOCAL_MACHINE\SOFTWARE\YourApp")]
回到頁首回到頁首

資料存取

ADO.NET SQL Server 資料提供者支援部分信任呼叫者。其他資料提供者包括 OLE DB、Oracle 和 ODBC 提供者,目前都要求完全信任呼叫者。

如果您使用 SQL Server 資料提供者連接至 SQL Server,則您的資料存取程式碼需要 SqlClientPermission。您可以使用 SqlClientPermission,限制傳遞至 SqlConnection 物件之連接字串允許使用的名稱 / 值配對範圍。下面的程式碼中,CheckProductStockLevel 方法利用額外的安全性檢查增加,確保連接字串中無法使用空白密碼。如果程式碼擷取密碼空白的連接字串,會擲回 SecurityException

[SqlClientPermissionAttribute(SecurityAction.PermitOnly, 
AllowBlankPassword=false)]
public static int CheckProductStockLevel(string productCode)
{
// 從登錄擷取連接字串
string connectionString = GetConnectionString();
  . . .
}

如需如何沙箱管理資料存取程式碼,以允許從部分信任 Web 應用程式使用 OLE DB 和其他資料提供者的詳細資訊,請參閱單元 9<配合 ASP.NET 使用程式碼存取安全性>。

回到頁首回到頁首

目錄服務

目前,使用 System.DirectoryServices 名稱空間中的類別存取 Active Directory 等目錄服務的程式碼,必須授與完全信任。不過,您可以使用 DirectoryServicesPermission ,限制存取權限類型,以及程式碼不能連接的特定目錄服務。

限制目錄服務存取

若要限制程式碼,可以使用 DirectoryServicesPermissionAttribute SecurityAction.PermitOnly。以下屬性確保程式碼只能連接至特定的 LDAP 路徑,而且只能瀏覽目錄。

[DirectoryServicesPermissionAttribute(SecurityAction.PermitOnly, 
Path="LDAP://rootDSE",
PermissionAccess=DirectoryServicesPermissionAccess.Browse)]
public static string GetNamingContext(string ldapPath)
{
DirectorySearcher dsSearcher = new DirectorySearcher(ldapPath);
dsSearcher.PropertiesToLoad.Add("defaultNamingContext");
dsSearcher.Filter = "";
SearchResult result = dsSearcher.FindOne();
return (string)result.Properties["adsPath"][0];
}

要求 DirectoryServicesPermission

若要記錄您程式碼的權限要求,並且確保程式碼存取安全性原則若未授與您的組件足夠的目錄服務存取權限時,組件就無法載入,請如以下範例所示,加入組件層級 DirectoryServicesPermissionAttribute SecurityAction.RequestMinimum 內容。

[assembly: DirectoryServicesPermissionAttribute(SecurityAction.RequestMinimum, 
Path="LDAP://rootDSE",
PermissionAccess=DirectoryServicesPermissionAccess.Browse)]
回到頁首回到頁首

環境變數

需要使用 System.Environment 類別讀取或寫入環境變數的程式碼,必須由程式碼存取安全性原則授與 EnvironmentPermission。這個權限類型可用於限制對特定具名環境變數的存取。

限制環境變數存取

若要限制程式碼,使其只能讀取特定的環境變數,可以使用 EnvironmentPermissionAttribute SecurityAction.PermitOnly 內容。以下屬性可確保程式碼只能從 usernameuserdomaintemp 變數讀取。

[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="username")]
[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="userdomain")]
[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="temp")]
public static string GetVariable(string name)
{
return Environment.GetEnvironmentVariable(name);
}

要求 EnvironmentPermission

若要記錄程式碼的權限要求,以及確保程式碼存取安全性原則若未授與您的組件足夠的環境變數存取權限時,無法載入組件,請如以下程式碼所示,加入組件層級 EnvironmentPermissionAttribute SecurityAction.RequestMinimum 內容。

[assembly: EnvironmentPermissionAttribute(SecurityAction.RequestMinimum,
Read="username"),
EnvironmentPermissionAttribute(SecurityAction.RequestMinimum,
Read="userdomain"),
EnvironmentPermissionAttribute(SecurityAction.RequestMinimum,
Read="temp")]
回到頁首回到頁首

Web 服務

呼叫 Web 服務的程式碼必須由程式碼存取安全性原則授與 WebPermissionWebPermission 會實際限制對任何 HTTP Internet 為基礎之資源的存取。

限制 Web 服務連線

若要將限制您的程式碼可以存取的 Web 服務,請使用 WebPermissionAttribute 搭配 SecurityAction.PermitOnly。例如,以下程式碼會確保 PlaceOrder 方法及其呼叫的任何方法,只能呼叫 http://somehost 網站上的 Web 服務。

[WebPermissionAttribute(SecurityAction.PermitOnly,
ConnectPattern=@"http://somehost/.*")]
[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="USERNAME")]
public static void PlaceOrder(XmlDocument order)
{
PurchaseService.Order svc = new PurchaseService.Order();
// Web 服務使用 Windows 驗證
svc.Credentials = System.Net.CredentialCache.DefaultCredentials;
svc.PlaceOrder(order);
}

上面的範例中,使用 WebPermissionAttribute 類別的 ConnectPattern 屬性。這可以讓您提供符合可以建立連線之位址範圍的規則運算式。前面顯示的 EnvironmentPermissionAttribute 之所以需要,是因為程式碼使用 Windows 驗證與預設憑證。

以下範例顯示如何使用 Connect 屬性,限制到特定 Web 服務的連線。

[WebPermissionAttribute(SecurityAction.PermitOnly,
Connect=@"http://somehost/order.asmx")]
回到頁首回到頁首

通訊端與 DNS

使用 System.Net.Sockets.Socket 類別直接使用通訊端的程式碼,必須由程式碼存取安全性原則授與 SocketPermission。另外,您的程式碼若使用 DNS 將主機名稱對應到 IP 位址,則需要 DnsPermission

您可以使用 SocketPermission ,限制對特定主機上特定連接埠的存取。也可以限制通訊端是否能用於接受連接,或初始化輸出連線,也可以限制傳輸通訊協定,例如傳輸控制通訊協定 (TCP) 或使用者資料包通訊協定 (UDP)。

限制通訊端存取

若要限制程式碼,使其只能有限制地使用通訊端,可以使用 SocketPermissionAttribute 搭配 SecurityAction.PermitOnly。以下屬性可確保程式碼只能使用 TCP 通訊協定,連接至特定主機上的特定連接埠。因為程式碼也會呼叫 Dns.Resolve 以解析主機名稱,所以程式碼也需要 DnsPermission

[SocketPermissionAttribute(SecurityAction.PermitOnly, 
Access="Connect", 
Host="hostname", 
Port="80", 
Transport="Tcp")]
[DnsPermissionAttribute(SecurityAction.PermitOnly, Unrestricted=true)]
public string MakeRequest(string hostname, string message)
{
Socket socket = null;
IPAddress serverAddress = null;
IPEndPoint serverEndPoint = null;
byte[] sendBytes = null, bytesReceived = null;
int bytesReceivedSize = -1, readSize = 4096;

serverAddress = Dns.Resolve(hostname).AddressList[0];
serverEndPoint = new IPEndPoint(serverAddress, 80);
socket = new Socket(AddressFamily.InterNetwork, 
SocketType.Stream, ProtocolType.Tcp);
bytesReceived = new byte[readSize];
sendBytes = Encoding.ASCII.GetBytes(message);
socket.Connect(serverEndPoint);
socket.Send(sendBytes);
bytesReceivedSize = socket.Receive(bytesReceived, readSize, 0);
socket.Close();
if(-1 != bytesReceivedSize)
  {
return Encoding.ASCII.GetString(bytesReceived, 0, bytesReceivedSize);
  }
return "";
}

要求 SocketPermission 與 DnsPermission

若要記錄您的程式碼的權限要求,以及確定若程式碼存取安全性原則未授與您組件足夠的通訊端或 DNS 存取權限時,無法載入組件,請如以下範例所示,加入組件層級 SocketPermissionAttribute DnsPermissionAttribute,搭配 SecurityAction.RequestMinimum

[assembly: SocketPermissionAttribute(SecurityAction.RequestMinimum, 
Access="Connect", 
Host="hostname", 
Port="80", 
Transport="Tcp")
DnsPermissionAttribute(SecurityAction.PermitOnly, Unrestricted=true)]
回到頁首回到頁首

Unmanaged 程式碼

呼叫 Unmanaged Win32 API 或 COM 元件的程式碼需要 Unmanaged 程式碼權限。這只應授與高度信任的程式碼。這是由 SecurityPermission 類型,將其 Flags 屬性設定為 SecurityPermissionFlag.UnmanagedCode 所定義。

下列呼叫 Unmanaged 程式碼的指導方針是以單元 7<建置安全的組件>的內容為基礎。

使用命名慣例表示風險

要求 Unmanaged 程式碼權限

使用沙箱管理 Unmanaged API 呼叫

小心使用 SupressUnmanagedCodeSecurityAttribute

使用命名慣例表示風險

使用下列命名慣例將您的 Unmanaged 程式碼分類,並為用於封裝 Unmanaged API 的類型加上字首。

Safe:這會識別沒有可能的安全性潛在威脅的程式碼。不論是惡意或其他任何程式碼,呼叫它都不會造成危害。傳回目前處理器滴答計數的程式碼就是一個例子。安全類別可以使用 SuppressUnmanagedCode 屬性註釋,這會關閉完全信任的程式碼存取安全性權限要求。

[SuppressUnmanagedCode]
class SafeNativeMethods {
[DllImport("user32")]
internal static extern void MessageBox(string text);
}

Native:這是有潛在危險的 Unmanaged 程式碼,但是程式碼有 Unamaged 程式碼權限的完全堆疊查核行程要求的保護。除非已經以 SupressUnmanagedCode 屬性抑制,否則這些是由 Interop 層隱含執行。

class NativeMethods {
[DllImport("user32")]
internal static extern void FormatDrive(string driveLetter);
}

Unsafe:這是有潛在危險的 Unmanaged 程式碼,宣告式抑制 Unamaged 程式碼權限的安全性要求。這些方法有潛在危險性。這些方法的任何呼叫者必須執行完整的安全性檢查,以確保用法安全且受到保護,因為不會執行堆疊查核行程。

[SuppressUnmanagedCodeSecurity]
class UnsafeNativeMethods {
[DllImport("user32")]
internal static extern void CreateFile(string fileName);
}

要求 Unmanaged 程式碼權限

增強名稱

[assembly: SecurityPermission(SecurityAction.RequestMinimum, 
UnmanagedCode=true)]

沙箱管理 Unmanaged API 呼叫

將特定組件中的 Unmanaged 程式碼呼叫隔離,並且將呼叫 Unmanaged 程式碼的組件保持在最少數目。然後,使用沙箱模式,確保只授與選取的組件 Unmanaged 程式碼權限。

若要以沙箱管理呼叫 Unmanaged 程式碼的 Managed 程式碼

1.

將呼叫 Unmanaged 程式碼的程式碼放在分開 (包裝函式) 組件。

2.

為組件加上增強名稱。
這可以輕易地將自訂程式碼存取安全性原則套用至組件。如需詳細資訊,請參閱單元 7<建置安全的組件>中的「增強名稱」。

3.

要求 Unmanaged 程式碼權限 (如前一節所述)。

4.

以完整權限要求授權呼叫程式碼。
您通常必須使用自訂權限,這個自訂權限表示您的組件公開的 Unmanaged 資源。例如:

(new EncryptionPermission(EncryptionPermissionFlag.Encrypt, 
storePermissionFlag.Machine)).Demand();

5.

在您的包裝函式類別中宣告 Unmanaged 程式碼權限:

(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert();

如需顯示如何呼叫 Unmanaged Win32 DPAPI 功能的完整範例實作,請參閱本指南<How To>一節中的<How To:建立自訂加密權限>。

小心使用 SuppressUnmanagedCodeSecurity

如果您的組件對 Unmanaged 程式碼有許多呼叫,與多個 Unmanaged 程式碼權限要求相關的額外效能負擔會造成問題。

在這種情況下,可以在 P/Invoke 方法宣告上使用 SupressUnmanagedCodeSecurity 屬性。這會使 Unmanaged 權限的完全需求由一個連結要求取代,這個要求只在 JIT 編譯時間發生一次。

與使用連結要求一樣,您的程式碼現在很容易受到偽裝攻擊的危害。若要降低風險,則只有在您的組件採取適當的預防措施,確定它不會受到惡意程式碼強制執行有害的作業時,才應該抑制 Unmanaged 程式碼權限要求。適當的反制措施範例之一,是您的組件是否要求更密切地反映 Unmanaged 程式碼執行之作業的自訂權限。

以 P/Invoke 使用 SuppressUnmanagedCodeSecurity

以下程式碼顯示如何將 SuppressUnmanagedCodeSecurity 屬性套用至 Platform Invocation Service (P/Invoke) 方法宣告。

public NativeMethods
{
// 這裡的 SuppressUnmanagedCodeSecurity 用法只適用於 FormatMessage
[DllImport("kernel32.dll"), SuppressUnmanagedCodeSecurity]
private unsafe static extern int FormatMessage(
int dwFlags, 
ref IntPtr lpSource, 
int dwMessageId,
int dwLanguageId, 
ref String lpBuffer, int nSize, 
IntPtr *Arguments);
}

配合 COM Interop 使用 SuppressUnmanagedCodeSecurity

針對 COM Interop 呼叫,必須在介面層級使用屬性,如以下範例所示。

[SuppressUnmanagedCodeSecurity]
public interface IComInterface
{
}
回到頁首回到頁首

委派

您呼叫委派方法時,無法事先知道委派方法將會執行什麼。如果您的組件支援部分信任呼叫者,則呼叫委派時必須採取額外的預防措施。您可以使用程式碼存取安全性進一步增強安全性。

考慮限制委派的權限

不要在呼叫委派之前宣告權限

考慮限制委派的權限

授與呼叫委派之程式碼的權限決定了委派的功能。如果您的程式碼擁有的權限比給予委派的程式碼更多,則呼叫者能夠使用較高的權限來執行程式碼。若要處理這個問題,可以在外部程式碼傳遞委派給您的點,以適當的權限要求授權程式碼,或者可以在呼叫委派的權限之前,使用拒絕或只允許堆疊修飾元來限制權限。例如,以下程式碼只會授與委派程式碼執行權限,以限制其功能。

// 委派定義
public delegate void SomeDelegate();
. . . 
// 在呼叫委派之前,只允許執行。這可以防止
// 委派程式碼存取資源,或執行其他特殊權限
// 操作
new SecurityPermission(SecurityPermissionFlag.Execution).PermitOnly();
// 現在呼叫「限制的」委派
SomeDelegate();
// 還原只允許堆疊修飾元
CodeAccessPermission.RevertPermitOnly();

不要在呼叫委派之前宣告權限

在呼叫委派之前宣告權限是很危險的,因為您不知道您呼叫委派時將要執行之程式碼的性質或信任等級。傳遞委派給您的程式碼是在呼叫堆疊上,因此可以使用適當的安全性要求檢查。但是,並無法知道授與委派程式碼本身的信任等級或權限。

如需安全地使用委派的詳細指引,請參閱單元 7<建置安全的組件>裡的「委派」一節。

回到頁首回到頁首

序列化

支援序列化的程式碼必須授與 SecurityPermission
Flag 屬性設定為 SerializationFormatter。如果您開發支援序列化的類別,而您的程式碼支援部分信任呼叫者,則應考慮使用額外的權限要求,限制可以將您物件的狀態序列化的程式碼。

限制序列化

如果您建立實作 ISerializable 介面的類別 (可以將您的物件序列化),可以在您的 ISerializable.GetObjectData 實作加入權限要求,授權嘗試將您的物件序列化的程式碼。如果您的程式碼支援部分信任呼叫者,這特別重要。

例如,以下程式碼片段使用 StrongNameIdentityPermission 要求,確定要求中只有與公開金鑰對應的特定私密金鑰所簽署的程式碼能將您物件的狀態序列化。

[StrongNameIdentityPermission(SecurityAction.Demand, 
PublicKey="00240000048...97e85d098615")]
public override void GetObjectData(SerializationInfo info, 
StreamingContext context)

如需安全地使用序列化的指引,請參閱單元 7<建置安全的組件>裡的「序列化」一節。

回到頁首回到頁首

總結

程式碼存取安全性可以讓您限制程式碼可以執行的作業、限制可以呼叫您程式碼的程式碼,以及辨識程式碼。在完全信任環境中,您的程式碼和呼叫您的程式碼,其權限集合有所限制,因此程式碼存取安全性就較不重要。

如果您的程式碼支援部分信任呼叫者,則安全性風險就更大。部分信任的案例中,程式碼安全性可以讓您降低某些額外風險,並允許您限制特殊權限程式碼。

回到頁首回到頁首

其他資源

有關其他詳細資訊,請參閱下列資源:

《Security in .NET: The Security Infrastructure of the CLR Provides Evidence, Policy, Permissions, and Enforcement Services (英文)》,作者 Don Box,MSDN Magazine,2002 年 12 月,網址:http://msdn.microsoft.com/msdnmag

《Security in .NET: Enforce Code Access Rights with the Common Language Runtime (英文)》,作者 Keith Brown,MSDN Magazine,2001 年 1 月,網址:http://msdn.microsoft.com/msdnmag

《.NET Framework Security (英文)》作者:LaMacchia、Lange、Lyons、Martin 與 Price,Addison Wesley 出版。


回到頁首回到頁首