反映的安全性考量
反映可讓您取得有關型別和成員的資訊,以及存取成員 (亦即,呼叫方法和建構函式、取得和設定屬性值、加入和移除事件處理常式等等)。 使用反映來取得型別和成員相關資訊的方式不受限制。 所有程式碼都可以使用反映來執行下列工作:
列舉型別和成員,並檢查它們的中繼資料 (Metadata)。
列舉及檢查組件和模組。
反之,使用反映來存取成員則有一些限制。 從 .NET Framework 4 版開始,只有受信任的程式碼能夠使用反映來存取安全性關鍵成員。此外,只有受信任的程式碼能夠使用反映來存取已編譯程式碼無法直接存取的非公用成員。最後,使用反映來存取安全關鍵成員的程式碼必須擁有安全關鍵成員所要求的任何使用權限,就像已編譯的程式碼一樣。
根據必要的使用權限,程式碼可以使用反映來執行下列存取種類:
存取非安全性關鍵的公用成員。
存取已編譯程式碼可存取的非公用成員 (如果這些成員非安全性關鍵的話)。 這類非公用成員的範例包括:
呼叫程式碼之基底類別的受保護成員 (在反映中,這稱為系列層級 (Family-level) 的存取)。
呼叫程式碼之組件中的 internal 成員 (Visual Basic 中的 Friend 成員) (在反映中,這稱為組件層級 (Assembly-level) 的存取)。
包含呼叫程式碼之類別的其他執行個體的私用成員。
例如,在沙箱應用程式定義域中執行的程式碼會限制為這份清單中所描述的存取,除非此應用程式定義域授與其他使用權限。
從 .NET Framework 2.0 版 Service Pack 1 開始,嘗試存取通常無法存取的成員就會產生目標物件的授權集再加上含有 ReflectionPermissionFlag.MemberAccess 旗標之 ReflectionPermission 的要求。 以完全信任執行的程式碼 (例如,從命令列啟動之應用程式中的程式碼) 一定可以滿足這些使用權限 (這受限於存取安全性關鍵成員的限制,如本文後面所述)。
此外,沙箱應用程式定義域可以授與含有 ReflectionPermissionFlag.MemberAccess 旗標的 ReflectionPermission,如本文後面的存取通常無法存取的成員一節所述。
存取安全性關鍵成員
如果某個成員具有 SecurityCriticalAttribute、屬於具有 SecurityCriticalAttribute 的型別或位於安全性關鍵組件中,它就是安全性關鍵成員。 從 .NET Framework 4 版開始,存取安全性關鍵成員的規則如下:
透明程式碼無法使用反映來存取安全性關鍵成員,即使此程式碼完全受信任也一樣。 系統會擲回 MethodAccessException、FieldAccessException 或 TypeAccessException。
以部分信任執行的程式碼會被視為透明。
不論安全性關鍵成員是直接由已編譯的程式碼存取,還是使用反映來存取,這些規則都相同。
從命令列執行的應用程式程式碼會以完全信任執行。 只要它沒有標記為透明,就可以使用反映來存取安全性關鍵成員。 當相同的程式碼以部分信任執行 (例如,在沙箱應用程式定義域中執行) 時,組件的信任層級就會決定它是否能存取安全性關鍵程式碼:如果此組件具有強式名稱而且安裝在全域組件快取中,它就是受信任的組件,而且能夠呼叫安全性關鍵成員。 如果它不受信任,就會成為透明,即使沒有標記為透明也一樣,而且它無法存取安全性關鍵成員。
如需 .NET Framework 4 之安全性模型的詳細資訊,請參閱 .NET Framework 4 中的安全性變更。
反映和透明度
從 .NET Framework 4 開始,Common Language Runtime 會根據許多因素判斷型別或成員的透明度層級,包括組件的信任層級以及應用程式定義域的信任層級。 反映會提供 IsSecurityCritical、IsSecuritySafeCritical 和 IsSecurityTransparent 屬性,可讓您探索型別的透明度層級。 下表顯示這些屬性的有效組合。
安全性等級 |
IsSecurityCritical |
IsSecuritySafeCritical |
IsSecurityTransparent |
---|---|---|---|
關鍵 |
true |
false |
false |
安全關鍵 |
true |
true |
false |
透明 |
false |
false |
true |
相較於檢查組件及其型別的安全性附註、檢查目前的信任層級,並嘗試複製執行階段規則,使用這些程序會簡單許多。 例如,相同的型別可能是安全性關鍵 (當它是從命令列執行時) 或安全性透明 (當它在沙箱應用程式定義域中執行時)。
MethodBase、FieldInfo、TypeBuilder、MethodBuilder 和 DynamicMethod 類別有類似的屬性 (對於其他反映和反映發出抽象,安全性屬性 (Attribute) 會套用到關聯的方法。例如,以屬性 (Property) 為例,它們就會套用至屬性 (Property) 存取子)。
存取通常無法存取的成員
若要使用反映來叫用無法根據 Common Language Runtime 之存取範圍規則對其存取的成員,您必須對程式碼授與兩種使用權限的其中一種:
若要允許程式碼叫用任何非公用成員:您的程式碼必須被授與包含 ReflectionPermissionFlag.MemberAccess 旗標的 ReflectionPermission。
注意事項 根據預設,安全性原則會拒絕來自網際網路的程式碼使用此權限。這項使用權限永遠不應該授與來自網際網路的程式碼。
若要在包含叫用之成員的組件授權集與包含叫用程式碼之組件的授權集相同,或包含叫用程式碼之組件的子集時,允許程式碼叫用任何非公用程式:您的程式碼必須被授與包含 ReflectionPermissionFlag.RestrictedMemberAccess 旗標的 ReflectionPermission。
例如,假設您授與某個應用程式定義域網際網路使用權限,再加上含 ReflectionPermissionFlag.RestrictedMemberAccess 旗標的 ReflectionPermission,接著執行內含兩個組件 (A 和 B) 的網際網路應用程式。
組件 A 可以使用反映來存取組件 B 的私用成員,因為組件 B 不包含任何未授與 A 的使用權限。
組件 A 無法使用反映來存取 .NET Framework 組件的私用成員 (如 mscorlib.dll),因為 mscorlib.dll 是完全受信任的,因此具有尚未授與組件 A 的使用權限。 當程式碼存取安全性在執行階段逐一查看堆疊時,就會擲回 MemberAccessException。
序列化
針對序列化 (Serialization) 的部分,含 SecurityPermissionAttribute.SerializationFormatter 旗標的 SecurityPermission 提供取得及設定可序列化型別之成員的能力,而不管其存取範圍為何。 這個使用權限可讓程式碼探索 (Discover) 並變更執行個體 (Instance) 的私用狀態 (除了被授與適當使用權限之外,型別還必須在中繼資料 (Metadata) 中被標記為可序列化)。
型別為 MethodInfo 的參數
請避免撰寫採用 MethodInfo 參數的公用成員,尤其是受信任的程式碼。 這類成員可能會更容易受到惡意程式碼的損害。 例如,試想在高度受信任的程式碼中採用 MethodInfo 參數的公用成員。 假設此公用成員針對提供的參數間接呼叫 Invoke 方法。 如果此公用成員不執行必要的使用權限檢查,由於安全性系統決定呼叫端受到高度信任,所以對於 Invoke 方法的呼叫一定都會成功。 即使惡意程式碼沒有使用權限來直接叫用方法,它仍然可以透過呼叫此公用成員,間接叫用方法。
版本資訊
從 .NET Framework 4 版開始,透明程式碼無法使用反映來存取安全性關鍵成員。
ReflectionPermissionFlag.RestrictedMemberAccess 旗標是在 .NET Framework 2.0 版 Service Pack 1 加入的。 對於使用反映存取非公用成員的程式碼,舊版 .NET Framework 需要 ReflectionPermissionFlag.MemberAccess 旗標。 這個使用權限永遠都不應該授與部分信任的程式碼。
從 .NET Framework 2.0 開始,使用反映來取得非公用型別和成員的相關資訊時,並不需要任何使用權限。 在舊版中,則需要含 ReflectionPermissionFlag.TypeInformation 旗標的 ReflectionPermission。