程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF中的代碼訪問安全性,第1部分

WCF中的代碼訪問安全性,第1部分

編輯:關於.NET

目錄

CAS 概覽

客戶端 CAS

部分信任的客戶端

客戶端請求

原始 WCF 請求

PartialTrustClientBase<T> 的結構化請求

分析 Invoke 的請求

實現客戶端結構化請求

在Microsoft® .NET Framework 1.0 中引入的代碼訪問安全性 (CAS) 可 能是 .NET 與非托管代碼相比唯一有所區別的功能。CAS 內置於 .NET Framework 的每個結構中,它影響托管代碼中的每項操作,這些是非托管代碼永 遠無法做到的。

Windows® Communication Foundation (WCF) 的第一個版本並不支持 CAS;System.ServiceModel 程序集不允許禁用了 CAS 支持的任何部分信任的調 用方。第二版在某些 HTTP 綁定中引入了對 CAS 的初步支持,但僅限於有限的 幾種方案。這一更改使我能夠著手編寫小型框架,可以在不影響 WCF 編程模型 或 CAS 的情況下提供對 CAS 的全面支持。

在有關 CAS 的兩個專欄的第一個中,我將簡要討論 WCF 中的代碼訪問安全 性,然後再介紹我的一個解決方案,它可以針對 WCF 服務啟用部分信任的客戶 端。

CAS 概覽

.NET Framework 定義了 24 種不同的安全權限,它們幾乎可以控制任何類型 的操作。這些權限包括文件 I/O、UI、反射、安全性、網絡、數據訪問等各個方 面。權限類型可以應用到特定的資源,例如文件 I/O 權限中的讀取特定文件的 權限,或者 UI 權限中的顯示特定類型窗口的權限。它也可以是完全拒絕(例如 拒絕執行任何文件 I/O 操作)或完全授予(例如不受限制的文件 I/O 訪問)。

權限被分組為權限集,並為每個程序集都分配了一個。.NET Framework 定義 了一些標准權限集,例如 FullTrust(暗含所有權限)或 Execution(僅可訪問 CPU 的權限)。管理員可以使用 .NET Configuration 工具定義自定義權限集, 而開發人員則可以通過編程方式來定義(或使用權限集文件,或使用應用程序所 需的權限集來定義)ClickOnce 應用程序清單。

加載程序集時,CLR 會為各個程序集分配其權限。程序集將提供某種形式的 身份證明以獲得這些權限。其中包括檢查程序集加載位置的基於來源的證明(例 如,所有來自全局程序集緩存 (GAC) 的代碼都被授予完全信任權限),或者檢 查程序集本身某些方面的基於內容的證明(例如,程序集的強名稱)。

每個應用程序域始終都會被分配一個被稱為應用程序域安全策略的權限集, 任何加載到該應用程序域的程序集都被限定為該權限集;否則將會出現安全異常 。新的應用程序域在開始時都具有 FullTrust 權限集,並且由於源自本地計算 機的所有代碼默認都將獲得 FullTrust 權限集,所以大多數基於 .NET 的應用 程序都以預設方式工作,與根本不使用 CAS 的效果完全一樣。這使得代碼(以 及用戶、數據、計算機甚至網絡)易於遭受各種形式的破壞,從病毒或蠕蟲之類 的安全攻擊到單純的人為錯誤。

以低於完全信任級別運行的代碼稱為部分信任的代碼。無論何時,托管代碼 的任何片斷試圖使用 .NET Framework 訪問資源或執行任何操作時(包括與非托 管代碼之間的互操作),.NET Framework 都會驗證包含該代碼的程序集是否具 有執行該操作所需的權限。如果該程序集缺少所需的權限,.NET Framework 將 拋出安全異常,從而中止該操作。

由於受信任的程序集可能會被惡意的、不太受信任的程序集引誘執行某些不 太受信任的程序集沒有權限執行的操作,所以僅對執行某項操作的程序集提出權 限要求尚嫌不夠。因此,.NET Framework 會遍歷整個調用方堆棧,確保堆棧中 的每個調用方都具有必要的權限。這種堆棧遍歷被稱為“安全要求” ,無論執行程序集的權限如何都必須滿足此要求。

您的代碼也可以聲明權限——即,聲明堆棧中的每個調用方都具 有所要求的權限。權限聲明具有終止堆棧遍歷的作用。您的代碼只能聲明它所具 有的權限,並且這還需要額外的特殊安全聲明權限。

在聲明一項權限的同時要求另一項就位是一種不錯的想法。開發人員可以使 用專門的權限類或使用匹配的屬性集通過編程方式要求或聲明權限。開發人員也 可以在程序集、類或方法級別主動拒絕權限。拒絕權限或僅允許執行代碼所需的 有限權限集可以減少受到引誘攻擊的幾率。有關代碼訪問安全性的更多信息,請 參閱我編寫的《Programming .NET Components 2nd Edition》一書中的第 12 章,我在其中使用超過 100 頁的篇幅介紹了這種基礎方法及其應用。

客戶端 CAS

在 .NET Framework 3.5 中,WCF 只允許在有限的幾種情況下執行部分信任 的代碼。WCF 在部分信任的情況下僅允許調用 HTTP 綁定 BasicHttpBinding、 WSHttpBinding 和 WebHttpBinding(不包括 WSDualHttpBinding),而且不提 供任何安全性或僅提供傳輸安全性。另外,對於 WSHttpBinding,消息安全性、 可靠的消息傳遞以及事務等各方面行為都被禁止。所有啟用部分信任的綁定都必 須使用文本編碼。客戶端不能使用類似診斷的附加 WCF 功能。為了能夠在部分 信任的環境中使用,System.ServiceModel 程序集必須在程序集定義中包括 AllowPartiallyTrustedCallers 屬性以允許部分信任的調用方:

[assembly: AllowPartiallyTrustedCallers]

在 WCF 的第一版中,省略此屬性將阻止所有有關部分信任的使用。在 .NET Framework 3.5 中,強制執行受限的支持功能集現在由綁定負責。每個非 HTTP 綁定都會主動要求其調用方的完全信任,無論是客戶端代理還是服務主機都要如 此。允許的 HTTP 綁定本身不需要完全信任,但需要根據使用的上下文來確定所 需的權限。在客戶端,這些綁定要求知道執行權限(帶有執行標記的安全權限) 以及連接到服務的權限(帶有到目標 URI 的連接標記的 Web 權限)。

除了這些對客戶端的要求以外,還有其他一些對配置的限制。例如,配置文 件不能包含任何對證書存儲區(用於客戶端證書憑據)的引用,因為訪問證書存 儲區會使 WCF 請求完全信任權限。

理想情況下,您會希望能夠領略 WCF 的完整功能,從分布式事務到可靠調用 、各種安全性憑據類型、通過 TCP 和進程間通信 (IPC) 渠道(例如命名管道) 進行的 intranet(甚至是同一台計算機)應用程序通信,完成所有這一切而無 需考慮 CAS——即,不必依賴於完全信任權限。

部分信任的客戶端

要使任何部分信任級別的客戶端都能夠使用所有 WCF 功能和綁定,必須阻止 綁定對完全信任的需要。實現此目標的唯一方法是使代理自我聲明完全信任。可 以通過 PermissionSetAttribute 聲明完全信任,只需使用 SecurityAction 枚 舉值的 Assert 標記並為權限名稱指定類型安全字符串 "FullTrust" 即可:

[PermissionSet(SecurityAction.Assert,Name = "FullTrust")]

盡管權限名稱是使用字符串指定的,但可以通過編譯器來增加安全性,因為 它可以檢驗允許的值。

此外,必須防止客戶端直接訪問 ClientBase<T> 基類的任何方法(這 仍然需要完全信任),因此代理需要隱藏常用的 Close 和 Dispose 方法。也可 以通過代理類自身訪問 ClientBase<T> 的方法或屬性(例如 Channel 或 構造函數),因為代理聲明了完全信任。

問題在於為了聲明完全信任,代理自身必須被授予完全信任,而這是部分信 任的客戶端無法首先提供的。因此,您需要將代理類從其自身程序集中分離出來 並將其標記為公有,然後授予該程序集完全信任。您可以使用 .NET Framework 2.0 Configuration 控制面板小程序完成此操作——只需使用一些基 於內容的證明(例如其強名稱)標識代理的程序集並授予程序集完全信任即可。

您還可以在客戶端的 GAC 中安裝代理程序集。因為所有來自 GAC 的程序集 都被授予完全信任,所以代理也將獲得完全信任。您還需記住要聲明 AllowPartiallyTrustedCallers 屬性;這將使部分信任的調用方能夠調用該程 序集。

最後,您需要將代理所使用的約定定義添加到代理的程序集中(並將約定標 記為公共)。這是必需的操作,因為 WCF 要求上級調用鏈的所有程序集都是完 全信任的,如果約定來自部分信任的程序集,請求將失敗。圖 1 顯示的是這些 約定和代理定義的示例。

Figure1通過代理聲明完全信任

[assembly: AllowPartiallyTrustedCallers]
[ServiceContract]
public interface IMyContract
{
 [OperationContract]
 void MyMethod();
}
[PermissionSet(SecurityAction.Assert,Name = "FullTrust")]
public class MyContractClient :
 ClientBase<IMyContract>,IMyContract,IDisposable
{
 public MyContractClient() {}
 public MyContractClient(string endpointName) : base(endpointName) {}
 /* More constructors */
 public void MyMethod() {
  Channel.MyMethod();
 }
 public new void Close() {
  base.Close();
 }
 void IDisposable.Dispose() {
  Close();
 }
}

客戶端請求

遺憾的是,圖 1 中顯示的技術具有潛在的安全漏洞。通過抑制 WCF 的安全 請求,任何部分信任的客戶端現在都可以調用任何 WCF 服務。假設有個客戶端 沒有被授予連接到 TCP 套接字或網站的權限。雖然該客戶端無法直接使用網絡 ,但是通過從受限制的客戶端環境越過 WCF 進行調用,它可以繞過此限制。

該問題的解決方案是使用專門的 ClientBase<T> 的子類,它一方面可 以聲明空白 WCF 請求以獲得完全信任,另一方面它將根據客戶端試圖執行的操 作來請求相應的特定安全權限。我的 PartialTrustClientBase<T> 類即 是這樣一種代理類,如圖 2 所示。

Figure2PartialTrustClientBase<T> 類

public abstract class PartialTrustClientBase<T> :
 ClientBase<T>,IDisposable
 where T : class
{
 [PermissionSet(SecurityAction.Assert,Name = "FullTrust")]
 public PartialTrustClientBase() {}
 [PermissionSet(SecurityAction.Assert,Name = "FullTrust")]
 public PartialTrustClientBase(string endpointName) :
  base(endpointName) {}
 [PermissionSet(SecurityAction.Assert,Name = "FullTrust")]
 public PartialTrustClientBase(Binding binding,
  EndpointAddress remoteAddress) :
  base(binding,remoteAddress) {}
 //Useful only for clients that want full-brunt raw demands from WCF
 protected new T Channel
 {
  [PermissionSet(SecurityAction.Assert,Name = "FullTrust")]
  get {
   return base.Channel;
  }
 }
 
 [PermissionSet(SecurityAction.Assert,Name = "FullTrust")]
 new public void Close() {
  base.Close();
 }
 void IDisposable.Dispose() {
  Close();
 }
 protected object Invoke(string operation,params object[] args) {
  if(IsAsyncCall(operation)) {
   DemandAsyncPermissions();
  }
  DemandSyncPermissions(operation);
  CodeAccessSecurityHelper.PermissionSetFromStandardSet(
   StandardPermissionSet.FullTrust).Assert();
  Type contract = typeof(T);
  MethodInfo methodInfo = contract.GetMethod(operation);
  return methodInfo.Invoke(Channel,args);
 }
 protected virtual void DemandAsyncPermissions() {
  CodeAccessSecurityHelper.DemandAsyncPermissions();
 }
 protected virtual void DemandSyncPermissions(string operationName) {
  this.DemandClientPermissions(operation);
 }
 bool IsAsyncCall(string operation) {
  if(operation.StartsWith("Begin")) {
   MethodInfo info = typeof(T).GetMethod(operation);
   object[] attributes = info.GetCustomAttributes(
    typeof(OperationContractAttribute),false);
   Debug.Assert(attributes.Length == 1);
   return (attributes[0] as
    OperationContractAttribute).AsyncPattern;
  }
  return false;
 }
}

使用 PartialTrustClientBase<T> 就像使用常規代理基類一樣。您仍 需對從中派生出的代理類授予完全信任權限,並允許部分信任的調用方。但是, 與圖 1 不同,PartialTrustClientBase<T> 不能在類級別上聲明完全信 任。相反,當需要時它可以在本地聲明完全信任。此外, PartialTrustClientBase<T> 也可以用於請求其他 CAS 權限。

如果您從 PartialTrustClientBase<T> 派生代理類,並如此處所示使 代理聲明完全信任,則在調用客戶端上將不會發出任何請求:

[PermissionSet(SecurityAction.Assert,Name = "FullTrust")]
public class MyContractClient :
 PartialTrustClientBase<IMyContract>,IMyContract
{
 public MyContractClient() {}
 public MyContractClient(string endpointName) :
  base(endpointName) {}
 public void MyMethod() {
  Channel.MyMethod();
 }
}

此代碼和 圖 1 所示代碼之間的唯一差別是新版本更簡潔,因為現在由 PartialTrustClientBase<T> 方法來實現 Close 和 Dispose 的隱藏。這 一新代理仍會抑制 WCF 發出的所有安全請求並使部分信任的客戶端暴露在一些 引誘攻擊面前,或使部分信任的客戶端比預期執行更多的操作。

原始 WCF 請求

對 PartialTrustClientBase<T> 更具安全意識的使用方法是不要使代 理聲明完全信任:

public class MyContractClient :
 PartialTrustClientBase<IMyContract>,IMyContract
{
 public MyContractClient() {}
 public MyContractClient(string endpointName) :
  base(endpointName) {}
 public void MyMethod() {
  Channel.MyMethod();
 }
}

要支持這種使用方法,PartialTrustClientBase<T> 必須在其構造函 數和 Close 方法中明確聲明完全信任。此外, PartialTrustClientBase<T> 將隱藏 ClientBase<T> 的 Channel 屬性,並在 get 訪問器中聲明完全信任。這將足以抑制 WCF 綁定完全信任請求 ,因為該請求是在構造代理時以及在打開和關閉其通道時(而不是在實際使用時 )發出的。通過這種方式構造代理會產生一種有趣的效果,即現在客戶端代碼將 遵守原始的 WCF 安全請求——也就是調度對服務的調用時所需的所 有安全請求!

例如,如果代理使用 TCP 綁定,則代理首先需要請求客戶端權限然後才能執 行(所有托管代碼都需要該權限)。第二,代理要求該客戶端具有連接到服務器 上所需端口的權限,以及不受限制的 DNS 權限(用於解析主機地址)。第三, 有一些與 TCP 的使用無關而與調用上下文相關的間接權限請求。

如果客戶端希望使用 Windows 安全機制並發送用戶的交互式身份信息,則代 理將請求訪問 USERNAME 變量的環境權限。如果客戶端希望發送另一種 Windows 憑據,則代理將請求安全權限來控制主體。如果客戶端希望異步調度調用或接收 雙向回調,則代理將請求控制策略和證明的權限(二者都是安全權限的標記,在 線程間切換調用時會需要它們)。如果客戶端希望使用可靠的消息傳遞,則代理 也將請求策略控制權限。如果客戶端使用的消息安全機制帶有用戶名憑據、帶有 服務證書協商但未驗證協商後的服務證書,則代理也將請求權限以控制策略和證 明。如果客戶端利用 WCF 診斷和跟蹤功能,則代理將請求訪問 COMPUTERNAME 環境變量(以便能夠進行跟蹤)和非托管代碼訪問(假定訪問日志文件,因此它 應該已有文件 I/O 權限)。

最後,代理還將請求客戶端權限以執行非托管代碼。非托管代碼訪問是高級 特權安全權限,僅授予最可信的代碼。授予非托管代碼訪問權限可能等價於禁用 CAS,因為非托管代碼在 CAS 中被豁免。設計用於在部分信任環境中工作的類和 框架在任何情況下都不應該請求非托管代碼訪問(即使它們使用互操作功能), 相反,它們應該請求更嚴格的權限來描述所執行的非托管操作的本性。TCP 通道 請求非托管代碼的原因很簡單,就是因為它在最初設計的時候沒有考慮到部分信 任的客戶端的情況。

即使能夠與某些權限類型完全匹配,某些 WCF 功能還是完全依賴於完全信任 的請求。任何傳播事務的嘗試都會請求完全信任(而不使用分布式事務權限), 而任何對證書存儲區的訪問都需要完全信任(而不是使用證書存儲區權限)。圖 3 顯示了在一些重要場合(例如事務、可靠性、診斷、異步調用、證書存儲區訪 問以及消息安全性)裡,當綁定被設置為其默認設置時,代碼(例如我編寫的上 一版本的 PartialTrustClientBase<T>)所調用的原始 WCF 安全請求。

Figure3原始 WCF 客戶端安全請求

方案 權限 TCP 執行和非托管代碼安全權限、不受限制的 DNS、連接到目標主 機端口的套接字權限 IPC 執行、非托管代碼、控制策略以及控制證明等安全權限 WS 和 WS Dual 執行和非托管代碼安全權限、連接到 URI 的 Web 權限 Basic 和 Web 執行安全權限、連接到 URI 的 Web 權限 MSMQ 執行和非托管代碼安全權限 異步調用、通過 TCP 的雙工通信 用於控制策略和證明的安全權限 基於 TCP 的 RM 用於控制策略的安全權限 交互式用戶憑據的 Windows 安全性 讀取 USERNAME 的環境權限 替代憑據的 Windows 安全性 用於控制主體的安全權限、讀取 USERNAME 的環境權限 診斷跟蹤 非托管代碼安全權限、讀取 COMPUTERNAME 的環境權限 帶有服務證書協商而不帶服務證書驗證的用戶名憑據、消息安 全性 用於控制策略和證明的安全權限、枚舉證書的存儲區權限 帶有服務證書協商而不帶服務證書驗證的用戶名憑據、TCP 消 息安全性 用於控制策略和證明的安全權限 任何證書存儲區訪問、事務傳播 完全信任 不帶服務證書協商或帶有服務證書驗證、證書憑據的用戶名憑 據、消息安全性 完全信任

不請求非托管代碼訪問的綁定只有基本綁定和 Web 綁定。默認情況下 WS 不 執行消息傳遞安全性的要求,從而導致請求非托管代碼訪問權限。

PartialTrustClientBase<T> 的結構化請求

如圖 3 所示,WCF 的關鍵方面(例如事務、可靠消息傳遞、無限制的消息安 全交互以及證書存儲區訪問)都需要完全信任,這使得我前一版本的 PartialTrustClientBase<T> 在部分信任環境中變得毫無意義。此外,通 過假定所有非 HTTP 綁定(以及具有消息傳遞安全性的 WS 綁定)來請求非托管 代碼訪問是不可接受的,因為這不符合 CAS 對部分信任客戶端的處理方式。

為了使部分信任的客戶端能適當、安全和正確地使用 WCF, PartialTrustClientBase<T> 提供了 Invoke 方法,其定義如下:

protected object Invoke(string operation,params object[] args);

Invoke 接受要調用的操作名稱及其參數,格式為以逗號分隔的對象參數數組 。Invoke 使用反射而不是使用 Channel 屬性進行調用。以下是使用 Invoke 方 法從 PartialTrustClientBase<T> 派生的代理:

public class MyContractClient :
 PartialTrustClientBase<IMyContract>,IMyContract
{
 public MyContractClient() {}
 public MyContractClient(string endpointName) :
  base(endpointName) {}
 public void MyMethod() {
  Invoke("MyMethod");
 }
}

Invoke 首先根據調用客戶端和目標服務端點的情況來請求適當的 CAS 權限 。如果客戶端被授予了這些權限(即請求沒有產生安全異常),則 Invoke 將以 編程方式聲明完全信任並繼續調用操作,只要客戶端具有調用服務所需的正確權 限。我將此類行為稱為“結構化的權限請求”。

Invoke 無法使用屬性來聲明完全信任,因為這將屏蔽它所請求的任何更加精 確的權限。相反,圖 2 中 Invoke 調用的實現首先檢查調用是否為異步調用( 使用操作約定的 AsyncPattern 標記)。如果是,Invoke 將使用 DemandAsyncPermissions 輔助方法請求適當的權限。然後,Invoke 使用 DemandSyncPermissions 輔助方法請求同步權限。對於調用本身而言,Invoke 使用我的 CodeAccessSecurityHelper 類通過編程方式聲明完全信任,如圖 4 所示。

Figure4CodeAccessSecurityHelper 類

public enum StandardPermissionSet {
 Internet,
 LocalIntranet,
 FullTrust,
 Execution,
 SkipVerification
}
public static class CodeAccessSecurityHelper {
 public static PermissionSet PermissionSetFromStandardSet(
  StandardPermissionSet standardSet);
 
 public static void DemandClientPermissions<T>(this ClientBase<T> proxy)
  where T : class;
 public static void DemandAsyncPermissions();
 
 //More members

PermissionSetFromStandardSet 方法接受代表標准 .NET 權限集之一的枚舉 值,並返回匹配的 PermissionSet 實例(請參見圖 5)。顧名思義, PermissionSet 是權限的集合,但它也可以表示超級權限集完全信任(它實際上 並不是一組獨立權限的集合,而更像是一個獨立的權限)。PermissionSet 類支 持 IStackWalk 接口,該接口允許您安裝堆棧遍歷修飾符,有時此類修飾符可以 聲明堆棧中的每個調用方都包含某些權限,以此來阻止在權限集中請求該權限。 堆棧遍歷修飾符將在安裝它的方法返回後自動被刪除。您也可以使用 PermissionSet 的靜態 RevertAssert 方法顯式刪除它。

Figure5PermissionState 和 PermissionSet

public enum StandardPermissionSet {
 Internet,
 LocalIntranet,
 FullTrust,
 Execution,
 SkipVerification
}
public static class CodeAccessSecurityHelper {
 public static PermissionSet PermissionSetFromStandardSet(
  StandardPermissionSet standardSet);
 
 public static void DemandClientPermissions<T>(this ClientBase<T> proxy)
  where T : class;
 public static void DemandAsyncPermissions();
 
 //More members

PartialTrustClientBase<T> 的 DemandAsyncPermissions 和 DemandSyncPermissions 輔助方法都使用與 CodeAccessSecurityHelper 相應的 方法來執行其請求。

圖 6 中的圖表顯示了 PartialTrustClientBase<T>.Invoke 作為所用 綁定的函數所發出的結構化請求以及方案的其他方面內容(例如使用事務、可靠 性、證書存儲區訪問、診斷、回調和異步調用等)。

Figure6PartialTrustClientBase<T> 的結構化安全請求

方案 權限 TCP 執行安全權限、不受限制的 DNS、連接到目標主機端口的套接 字權限 IPC 執行、控制策略以及控制證明等安全權限 WS、Basic 執行安全權限以及連接到 URI 的 Web 權限 WS-Dual 執行安全權限以及連接到 URI 的 Web 權限、接受對回調地址 進行回調的 Web 權限、最小化 ASP.NET 托管權限 MSMQ 執行安全權限、發送到隊列的 MSMQ 權限 基於 TCP 的 RM 用於控制策略的安全權限 通過 TCP 進行雙工通信、異步調用 (AsyncPattern)、用戶名 憑據、帶有服務證書協商而不帶服務證書驗證的消息安全性 用於控制策略和控制證明的安全權限 交互式用戶憑據的 Windows 安全性 讀取 USERNAME 的環境權限 替代憑據的 Windows 安全性 用於控制主體的安全權限 事務傳播 不受限制的分發事務權限 不帶服務證書協商或帶有服務證書驗證、證書憑據的用戶名憑 據、消息安全性 用於枚舉存儲區、打開存儲區以及枚舉證書的存儲區權限 診斷跟蹤 用於讀取 COMPUTERNAME 的環境權限、用於路徑發現、附加、 寫入日志文件的文件 I/O 權限

分析 Invoke 的請求

我在發出結構化請求時主要基於以下幾個要素。第一,我會盡可能嘗試向在 客戶端上由 WCF 發起的原始請求靠攏。這就是說,由於 WCF 並非是為了全面的 部分信任用途而設計,所以我需要根據實際情況對其進行改動。圖 6 中列出的 綁定和方案都不需要請求完全信任或非托管代碼訪問。.NET Framework 中提供 了各種方法,可以在部分信任環境的相似上下文中使用,而且我依賴於與它們相 同的請求。最後,在其他一些情況下,為抵消抑制完全信任請求帶來的不利狀況 ,我借助過去的經驗、對 CAS 的熟悉程度和常識將 WCF 活動映射到對專用權限 類型的請求。

當使用 WS-Dual 綁定時,Invoke 使用與任何其他 HTTP 綁定一樣的方法請 求 Web 權限以連接到目標端點。但是,為允許托管回調對象,它還需要請求最 低的 ASP.NET 托管權限和 Web 權限才能接受對回調地址的調用。當使用 MSMQ 綁定時,Invoke 將請求 MSMQ 權限以便將消息發送到目標隊列。

在任何將客戶端事務傳播到服務器的嘗試中,Invoke 都可以請求不受限制的 分發事務權限。在這種情況下,事務感知綁定被采用、事務流在綁定中被啟用、 事務流在操作級別得到允許,並且客戶端中會包含環境事務。

任何嘗試通過代理來訪問證書存儲區的行為都會觸發相應的權限請求,以枚 舉證書存儲區、打開存儲區以及枚舉存儲區中的證書。出現這種情況的場合包括 當客戶端使用證書憑據時、當使用了消息安全且客戶端需要驗證協商服務證書時 ,或者當客戶端不協商證書而是改為僅加載用於保護消息安全的證書時。

當客戶端使用 WCF 診斷時,Invoke 將請求用於讀取計算機名稱的環境權限 和用於記錄並跟蹤文件使用情況的文件 I/O 權限。

實現客戶端結構化請求

如前所述,請求是通過 CodeAccessSecurityHelper 執行的,它的部分實現 如圖 7 所示。.NET Framework 中的所有權限類型都支持 IPermission 接口:

Figure7實現 CodeAccessSecurityHelper(部分)

public static class CodeAccessSecurityHelper
{
 public static void DemandClientPermissions<T>(this ClientBase<T> proxy,
  string operationName) where T : class
 {
  DemandClientConnectionPermissions(proxy.Endpoint);
  DemandTransactionPermissions(proxy.Endpoint,operationName);
  DemandTracingPermissions();
  DemandClientSecurityPermissions(proxy);
  DemandEnvironmentPermissions(proxy);
  DemandClientStorePermissions(proxy.Endpoint);
 }
 internal static void DemandClientConnectionPermissions(
  ServiceEndpoint endpoint)
 {
  PermissionSet connectionSet = new PermissionSet (PermissionState.None);
  if(endpoint.Binding is NetTcpBinding)
  {
   connectionSet.AddPermission(new SocketPermission(
    NetworkAccess.Connect,TransportType.Tcp,
    endpoint.Address.Uri.Host,endpoint.Address.Uri.Port));
    
   connectionSet.AddPermission(new DnsPermission(
    PermissionState.Unrestricted));
  }
  /* Rest of the bindings */
  connectionSet.Demand();
 }
 internal static void DemandTransactionPermissions(
  ServiceEndpoint endpoint)
 {
  DemandTransactionPermissions(endpoint,null);
 }
 internal static void DemandTransactionPermissions(
  ServiceEndpoint endpoint, string operationName)
 {
  bool transactionFlow = false;
  bool flowOptionAllowed = false;
  if(endpoint.Binding is NetTcpBinding)
  {
   NetTcpBinding tcpBinding = endpoint.Binding as NetTcpBinding;
   transactionFlow = tcpBinding.TransactionFlow;
  }
  /* Checking other bindings */
  if(transactionFlow)
  {
   if(Transaction.Current != null)
   {
    //If operationName is null then at least one operation
    //needs to allow flow to trigger demand
    foreach(OperationDescription operation in
     endpoint.Contract.Operations)
    {
     string name = operationName ?? operation.Name;
     if(name != operation.Name)
     {
      continue;
     }
     foreach(IOperationBehavior behavior in operation.Behaviors)
     {
      if(behavior is TransactionFlowAttribute)
      {
       TransactionFlowAttribute attribute =
        behavior as TransactionFlowAttribute;
       if(attribute.Transactions !=
        TransactionFlowOption.NotAllowed)
       {
        flowOptionAllowed = true;
        break;
       }
      }
     }
     if(flowOptionAllowed)
     {
      break;
     }
    }
    if(flowOptionAllowed)
    {
     IPermission distributedTransactionPermission =
      new DistributedTransactionPermission(
      PermissionState.Unrestricted);
     distributedTransactionPermission.Demand();
    }
   }
  }
 }
 //Rest of the implementation
}
  
public interface IPermission : ...
{
  void Demand();
  //More members
}

要請求任何權限,可實例化權限對象並調用其 Demand 方法的實現。構造權 限集時,可以為其添加單獨的權限,然後調用該權限集的 Demand 方法來請求其 中的所有權限。

CodeAccessSecurityHelper 的擴展方法 DemandClientPermissions 代表 PartialTrustClientBase<T> 執行批量請求(使用的擴展可用於任何代理 類)。它包含一系列輔助方法,它們都可以分別請求某方面的權限。圖 7 顯示 了 DemandClientConnectionPermissions 的代碼,它用於根據綁定來請求連接 權限。它通過代理檢查所使用的綁定類型,並向權限集對象中添加適當的權限。 然後,它將調用該權限集的 Demand 方法。

DemandTransactionPermissions 方法首先核實代理是否正在使用事務流綁定 功能。如果是,它還將核實調用客戶端是否真的包含要傳播的環境事務 (Transaction.Current 不為空)。如果是,它將掃描該端點約定中操作的集合 ,以尋找當前調用的操作。找到操作後,DemandTransactionPermissions 將檢 索該操作的操作行為集合。DemandTransactionPermissions 將檢查每種行為, 尋找 TransactionFlowAttribute。如果該屬性被配置為允許事務傳播,則 DemandTransactionPermissions 會請求分發的事務權限。 DemandClientPermissions 還可通過相似的方式使用其他輔助方法請求適當的權 限。

最後,為啟用部分信任的客戶端和回調,我定義了 PartialTrustDuplexClientBase<T,C> 類,並按照與 PartialTrustClientBase<T> 非常相似的方法實現了它,不同之處在於它 為客戶端接收回調加入了雙工支持。

至此,我們已經快速介紹了 WCF 中的代碼訪問安全,還介紹了可啟用部分信 任的客戶端並具有相應代碼級權限級別的解決方案。我將在下一部分中詳細介紹 部分信任的服務和部分信任的宿主。您還將了解到一些 WCF 和 .NET Framework 的高級編程技術。

請將您想詢問的問題和提出的意見發送至[email protected].

Juval Lowy 是 IDesign 的一名軟件架構師,提供 WCF 培訓和 WCF 體系結 構咨詢服務。另外,他還是 Microsoft 硅谷地區的區域總監。他最近編寫了一 本名為《Programming WCF Services》的新書。您可以通過 www.idesign.net 與 Juval 聯系。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved