在 Microsoft .NET Framework 4 中,公共語言運行時 (CLR) 安全模型發生了不少變化。其中一項變 化,即采用 Level2 透明性(與 Silverlight 的安全模型非常相似)很可能影響 AllowPartiallyTrustedCallers (APTCA) 庫的作者。
該影響源於 APTCA 的基礎工作在 CLR v4 中發生了變化。APTCA 屬性保持了向部分受信任調用方提供 完全可信任庫的功能,但具體的提供方式發生了變化,因此,有可能需要對 APTCA 庫代碼進行一些修改 。
注意:本篇文章中的v2 是指 CLR v2,它包括從 .NET Framework versions 2.0 到 3.5 SP1 的所有 版本。
V4 之前的APTCA
在 v4 之前,通過對所有入口點的完全信任隱式鏈接要求,所有簽名程序集都受到保護,不提供給部 分受信任調用方。這意味著,任何部分受信任代碼在嘗試訪問強名稱的程序集時都因為安全性異常而失敗 。這會阻止部分受信任調用方惡意調用(存在潛在風險的)完全受信任代碼。
將 AllowPartiallyTrustedCallers 屬性添加到簽名的完全受信任庫後,可對這些庫移除這些隱式鏈 接要求,從而可供部分受信任方使用。因此,APTCA 庫通過 APTCA 公開的方法允許部分受信任代碼以受 控的方式訪問特權操作。APTCA 作者有責任確保通過這種方式只向部分受信任方公開安全操作,任何存在 潛在風險的操作都應通過隱式鏈接要求或完整要求加以保護。
V4 中的APTCA
AllowPartiallyTrustedCallers 屬性已發生變化。v4 再不用考慮鏈接要求。事實上,曾在 v2 中存 在的對簽名庫的隱式鏈接要求已不復存在。而 v4 中所有完全受信任程序集在默認情況下都屬於 SecurityCritical。另一方面,v4 中所有部分受信任程序集在默認情況下均屬於 SecurityTransparent 。正如下一部分的透明性概述中所言,SecurityTransparent 代碼將無法調用 SecurityCritical 代碼。
因此,新 v4 透明性系統對完全受信任代碼提供與舊的鏈接要求相同的保護;由於 SecurityCritical 和 SecurityTransparent 是自動應用的透明性級別,部分受信任代碼默認情況下無法調用完全受信任的 庫。
您可能猜到,v4 中對 AllowPartiallyTrustedCallers 的更改與此有關。在 v4 中,APTCA 的變化是 從應用 SecurityCritical 的程序集取消了自動 SecurityCritical 行為。因此程序集默認為 SecurityTransparent,但允許 APTCA 程序集作者在必要時對特定類型和方法應用更加具體的 SecurityCritical 和 SecuritySafeCritical 屬性。
透明性速成教程
熟悉 Silverlight 安全模型的讀者對於 SecurityTransparent 和 SecurityCritical 等透明性屬性 的作用並不會感到陌生,因為新 v4 透明性模型與 Silverlight 安全模型非常相似。
讓我們先來了解一下三個主要的透明性屬性:SecurityTransparent、SecuritySafeCritical 和 SecurityCritical。
SecurityTransparent:標記為 SecurityTransparent 的代碼從安全性角度而言是可靠的。它不能完 成任何危險操作,例如聲明權限、執行無法驗證的代碼或調用本機代碼。它也不能直接調用 SecurityCritical 代碼。
如上文所述,出於安全的考慮,所有部分受信任代碼都強制為 SecurityTransparent。這也是 APTCA 庫的默認透明性。
SecurityCritical:與 SecurityTransparent 不同,SecurityCritical 代碼能夠執行任何所需操作 。它能夠執行聲明、調用本機代碼和其他操作。它能夠調用其他方法,且不受透明性標記的限制。
只有完全受信任代碼才能為 SecurityCritical。事實上,(非 APTCA)完全受信任代碼默認情況下屬 於 SecurityCritical,從而保護其免受透明的部分受信任調用方的調用。
SecuritySafeCritical:SecuritySafeCritical 代碼起著橋梁的作用,它允許透明代碼調用關鍵方法 。SecuritySafeCritical 代碼與 SecurityCritical 代碼的權限相同,但它可由 SecurityTransparent 代碼調用。因此,SecuritySafeCritical 代碼必須以安全方式公開基礎 SecurityCritical 方法(以避 免一些部分受信任的惡意代碼嘗試通過 SecuritySafeCritical 層攻擊這些方法),這一點極為重要。
與 SecurityCritical 代碼一樣,SecuritySafeCritical 代碼必須完全受信任。
圖 1 描述了 SecurityTransparent、SecuritySafeCritical 和 SecurityCritical 代碼之間的內在 關系。
圖 1 SecurityTransparent、SecuritySafeCritical 和 SecurityCritical 代碼之間的內在關系
注意:除了圖中所示的轉換關系外,所有透明性級別都可以訪問自身以及任何重要性級別較低的代碼 (例如,SecuritySafeCritical 代碼可以訪問 SecurityTransparent 代碼)。 AllowPartiallyTrustedCallers 屬性使整個程序集默認情況下是 SecurityTransparent,因此,程序集 作者必須將需要執行特權操作的方法專門標記為 SecurityCritical 或 SecuritySafeCritical。如果不 進行這樣的標記,APTCA 作者將會發現其代碼因 MethodAccessExceptions、TypeAccessExceptions 和其 他錯誤導致執行失敗,這些錯誤指示 APTCA 庫正在嘗試從 SecurityTransparent 代碼調用危險 API。
此處僅對該模型進行了簡介,如需了解詳細信息,請訪問 MSDN 文檔和 Andrew Dai 之前的文章“CLR 全面透徹解析”(文章地址:msdn.microsoft.com/magazine/ee677170.aspx)。
從 V2 遷移到 V4:要應用的屬性
從 v2 APTCA 程序集遷移到 v4 的大部分工作涉及標識正確的透明屬性並將其應用到需要它們的方法 。下列指導原則對屬性的選擇進行了說明。
SecurityTransparent:不能執行任何安全敏感操作的代碼應屬於 SecurityTransparent。
與其他透明性設置不同,SecurityTransparent 行為在 APTCA 程序集中是默認行為屬性,因此無需對 其進行顯式標記。如果無其他屬性,代碼將默認為透明。
透明代碼的一個優勢在於其安全性(因為不允許執行危險操作),因此對它的安全性審查沒有 SecurityCritical 尤其是 SecuritySafeCritical 代碼那麼嚴格。建議您盡量對代碼使用 SecurityTransparent。
在 SecurityTransparent 代碼中禁止執行下列操作:
調用 SecurityCritical 方法
聲明權限或權限集
使用無法驗證的代碼
調用非托管代碼
重寫 SecurityCritical 的虛擬方法
實現 SecurityCritical 接口
從任何非 SecurityTransparent 類型派生
SecuritySafeCritical:如果代碼可以由部分受信任調用方調用,但又需要能夠調用潛在危險的API, 則此代碼應標記為 SecuritySafeCritical。通常,要求權限的方法屬於此類,因為它們代表介於部分受 信任代碼和特權操作之間的受保護邊界。
由於 SecuritySafeCritical 代碼允許部分受信任調用方間接訪問危險 API,這是一個非常強大的屬 性,應對其應用持謹慎和保守的態度。SecuritySafeCritical 代碼必須以特定、安全的方式對其調用方 公開 SecurityCritical 功能。通常,一個較好的做法是讓 SecuritySafeCritical 代碼包含要求,以確 保調用方能夠訪問 SecuritySafeCritical 代碼將使用的特定資源。對 SecuritySafeCritical 代碼而言 ,驗證輸入和輸出也非常重要,從而確保不傳遞無效的值,以及任何返回的信息能夠安全地交給部分受信 任調用方。
由於可能存在安全風險,建議盡量少用 SecuritySafeCritical 代碼。
SecurityCritical:如果向部分受信任調用方公開代碼存在風險,則此代碼應標記為 SecurityCritical。之前受鏈接要求保護的方法可能需要此屬性。
SecurityCritical 代碼的危險性低於 SecuritySafeCritical,因為透明調用方(部分受信任方)不 能直接對其進行調用。然而,此代碼能夠執行很多高安全性操作。因此,為了將對安全審查的需求降至最 低,最好也盡量少用 SecurityCritical 代碼。
一般原則是盡可能將代碼標記為 SecurityTransparent。其他代碼則應標記為 SecurityCritical,除 非明確期望透明代碼將通過其訪問 SecurityCritical 代碼,則將該代碼標記為 SecuritySafeCritical 。
使用 SecAnnotate.exe
為了幫助正確應用透明性屬性,可以使用新的.NET Framework SDK 工具 Security Annotator (SecAnnotate.exe)。此工具使用用戶的二進制數據(或二進制集合),並提供有關在何處應用透明性屬 性的指導。該工具在將 APTCA 庫遷移到 v4 時非常有幫助。
SecAnnotate 的工作原理是數次遍歷目標二進制數據,查找根據 CLR 規則需要標上透明性屬性的方法 。在隨後的遍歷中,該工具會根據之前遍歷中建議的修改而查找必要的屬性。例如,考慮下面一小段代碼 (假定其來自 APTCA 程序集):
static void Method1()
{
Console.WriteLine("In method 1!");
Method2();
}
static void Method2()
{
PermissionSet ft = new PermissionSet(PermissionState.Unrestricted);
ft.Assert();
DangerousAPI();
PermissionSet.RevertAssert();
}
SecAnnotate.exe 將立即注意到 Method2 不能為透明,因為它聲明了一些權限。第一次遍歷後,工具 將確定 Method2 不是 SecurityCritical 就是 SecuritySafeCritical(除非透明代碼需要專門訪問此方 法,否則它很有可能屬於 SecurityCritical)。
第一次遍歷二進制數據時,Method1 似乎並未引起工具的注意。但在第二次遍歷時,工具將注意到 Method1 正在調用 Method2,正如 SecAnnotate 在第一次遍歷時所建議的,Method2 成為 SecurityCritical。鑒於此,Method1 同樣應該屬於 SecurityCritical(作者也可根據自己的判斷將其 歸於 SecuritySafeCritical)。經過兩次遍歷後,SecAnnotate 建議將這兩種方法都標記為 SecurityCritical。
理解 SecAnnotate.exe 輸出
Security Annotator 的輸出是 XML 文件,其中包含發現的問題以及建議的解決方法。有時, Security Annotator 可能在後續的遍歷中推翻之前的建議。如果出現這種情況,XML 文件將顯示兩次的 建議。此時,您需要查看遍歷編號,以清楚哪一項是最新建議,即正確的建議。
例如,讓我們來考慮一下圖 2 中的Security Annotator 輸出。注意:在方法 Logging.MethodA 的批 注標記下有兩個元素:一個 SecuritySafeCritical 標記和一個 SecurityCritical 標記。這表示 SecAnnotate 在分析過程中建議此方法使用 SecurityCritical 和 SecuritySafeCritical 屬性。
圖 2 Security Annotator 輸出
<requiredAnnotations>
<assembly name="Logging">
<type name="Logging">
<method name="MethodA()">
<annotations>
<safeCritical>
<rule name="MethodsMustOverrideWithConsistentTransparency">
<reason pass="2" sourceFile="d:\repro\aptca\logging.cs" sourceLine="67">Critical method Logging.MethodA()’ is overriding transparent or safe critical method ‘Logging.MethodA()’ in violation of method override rules. Logging.MethodA()’ must become transparent or safe-critical in order to override a transparent or safe-critical virtual method or implement a transparent or safe-critical interface method.</reason>
</rule>
</safeCritical>
<critical>
<rule name="TransparentMethodsMustNotSatisfyLinkDemands">
<reason pass="1" sourceFile="d:\repro\aptca\logging.cs" sourceLine="68">Security transparent method Logging.MethodA()’ satisfies a LinkDemand for ‘FileIOPermissionAttribute’ on method ‘Logging.set_LogLocation(System.String)’. Logging.MethodA()’ should become critical or safe-critical in order to call ‘Logging.set_LogLocation(System.String)’.</reason>
</rule>
</critical>
</annotations>
</method>
</type>
</assembly>
</requiredAnnotations>
對 SecurityCritical 元素的解釋表示由於此方法正在調用受鏈接要求保護的內容,因此它不是 SecurityCritical 就是 SecuritySafeCritical。SecAnnotate.exe 默認情況下建議選擇 SecurityCritical,因為它更加安全。注意:此處的遍歷屬性值為 1,這表示此建議來自 SecAnnotate.exe 對代碼的第一次遍歷。
接著對 SecuritySafeCritical 的建議表示 MethodA 正在重寫透明基方法,因此必須為 SecurityTransparent 或 SecuritySafeCritical(必須具有與基方法相同的可訪問性)。綜合考慮此信 息和之前的建議,SecAnnotate.exe 建議 MethodA 采用 SecuritySafeCritical。
注意:pass="2" 表示此建議來自 SecAnnotate.exe 對代碼的第二次遍歷。這是因為在第一次遍歷中 ,工具不知道 MethodA 不能為透明,因此沒有意識到對 SecuritySafeCritical 的要求。
由於 SecuritySafeCritical 建議源於第二次(最新)遍歷,因此在此例中它是正確的批注。
SecAnnotate.exe 最佳實踐
如果 SecurityCritical 和 SecuritySafeCritical 都是正確的標記,Security Annotator 優先選擇 代碼已有的屬性,其次選擇 SecurityCritical,因為它的風險較小。不幸的是,這樣做得到的代碼雖然 很安全,但在沙盒中卻不可利用,因為將對部分受信任調用方堵塞所有入口點。
請牢記,如果 API 應由透明/部分受信任代碼直接調用並且已考慮此點進行過安全審查,則這些 API 適用 SecuritySafeCritical。由於 Security Annotator 無法知道哪些 API 計劃由部分受信任調用方調 用,無法知道通過這種方式調用是否安全,因此它只會將很少的API 標記為 SecuritySafeCritical。庫 作者必須手動將 SecuritySafeCritical 屬性應用到某些方法,即便在使用 Security Annotator 時也如 此。
由於在透明代碼中發生一次受到禁止的操作,就有可能導致在 Security Annotator 的後續遍歷中繁 衍出許多 SecurityCritical 標記而不是導致策略性地放置 SecuritySafeCritical 標記,因此為 SecAnnotate.exe 使用 /p 命令行開關是不錯的選擇。開關 /p:x(x 表示數字)指示 Security Annotator 僅運行 x 次遍歷,無需運行到完成所有必需的更改為止。下面是使用 Security Annotator 的一種好方法:
運行 SecAnnotate.exe /p:1 /d:<被引用程序集路徑> <FileName.dll>
a. 這將在需要時添加透明性屬性,但僅針對單次遍歷。如果到此為止,作者可以手動檢查屬性。
b. 默認情況下,SecAnnotate.exe 僅在 GAC 中尋找它所批注的程序集的依賴關系。其他程序集必須 通過 /d 開關指定路徑。
使用建議屬性更新庫的源文件。但也要考慮出現多個屬性的情況,然後選擇正確的屬性。在某些情況 下,盡管 SecAnnotate 偏好 SecurityCritical,但 SecuritySafeCritical 才是正確的屬性。
重新生成程序集,並從步驟 1 重復(不帶 /p:1)。可以反復使用 /p:1 重新運行程序,但您無需這 樣做,因為在第一次迭代步驟 2 之後便已得到所需 SecuritySafeCritical 屬性。
包含開發人員手動干預的迭代流程將產生批注正確的程序集,從而得到最多的透明代碼。
確定和檢查 SecuritySafeCritical API
如前文所述,SecAnnotate.exe 通常建議 API 不是 SecurityCritical 就是 SecuritySafeCritical 。主要區別在於部分受信任調用方是否能夠安全地調用 API。如果 API 經過所有驗證可以確保基礎關鍵 或本機 API 能夠被安全(例如通過要求或輸入和輸出驗證)調用,則它可以標記為 SecuritySafeCritical,此屬性有時更加理想,因此它允許 API 的調用方是透明的。另一方面,如果惡 意代碼有可能通過 API 訪問受保護的資源,則該 API 必須保持為 SecurityCritical。
有必要仔細檢查所有 SecuritySafeCritical 代碼,以確定在其公開給部分受信任調用方後的安全影 響。盡管 SecuritySafeCritical 和 SecurityCritical 代碼都應盡量少用,但如果對於哪個屬性合適心 存疑慮,則 SecurityCritical 是較安全的選擇。
應用透明性屬性
應用透明性屬性和在代碼中應用任何其他 .NET 屬性一樣簡單。MSDN 中提供了下列屬性類型的使用文 檔:
SecurityTransparentAttribute
注意:此屬性只應用於程序集級別。在此情況下,它意味著程序集中的所有類型和方法都是透明的。 在類型或方法級別沒有必要使用此屬性,因為它是 APTCA 程序集中的默認透明性設置。
SecuritySafeCriticalAttribute
SecurityCriticalAttribute
在 C# 中,屬性的應用與下列代碼類似:
[SecurityCritical]
public static void Method1()
{ /* Do something potentially dangerous*/ }
[SecuritySafeCritical]
public static void Method2()
{ /* Do something potentially dangerous in a safe way that can be called from partial trust */ }
Level1 和 Level2
關於透明性和 APTCA 的最後一點說明是:可以通過使用程序集級別的屬性來使用較舊的v2 APTCA 行 為而非新 v4 行為。但不建議您這麼做,這是因為新模型更安全,更易於審核,並且在 Silverlight 和 桌面 CLR 之間通用。當然,在遷移之前,有時在短期內還是會存在兼容性問題。在這種情況下,您可以 使用 SecurityRules 屬性強制程序集使用較舊的v2 規則。
SecurityRules 屬性采用 SecurityRuleSet 枚舉類型的參數。SecurityRuleSet.Level1 指定兼容性 。SecurityRuleSet.Level2 指定新模型,但 Level2 屬性並非必需,因為它是默認設置。但它能夠用於 顯式指示使用中的透明性規則集,並防止將來對 .NET Framework 默認規則集的更改。
在 C# 中,此屬性的應用如下所示:
[assembly:SecurityRules(SecurityRuleSet.Level1)]
常見缺陷
APTCA 庫作者在從 v2 遷移到 v4 時應注意下列常見問題:
SecAnnotate.exe 會建議將 LinkDemands 更改為 SecurityCritical 屬性(這與對 FullTrust 的 LinkDemands 非常相似)。但如果類型(而非方法)原來由 LinkDemand 保護,這與在 v4 中向類型應用 SecurityCritical 不同。最好對類型的所有成員應用 SecurityCritical,因為這與 v2 類型級別的 LinkDemand 更為相似。
注意,有些部分受信任代碼應該可以滿足某些低權限 LinkDemand,將這些 LinkDemand 轉換為 SecurityCritical 不一定是最好的選擇。如果 LinkDemand 針對的是低權限(例如,對特定安全路徑的 讀取權限),您最好移除 LinkDemand 並將其替換為針對相同權限的完整要求。這將使部分受信任代碼能 夠繼續調用 API(但該要求將確保僅具有足夠權限的部分受信任代碼才能進行調用)。
通常,類型級別的透明性屬性也會應用到它們修改的類型的成員。最外層的屬性將取代其他屬性。因 此,如果方法所在的類型應用了 [SecuritySafeCritical],那麼對該方法應用 [SecurityCritical] 將 無效。通常,[SecuritySafeCritical] 在類型級別不是一個有用的屬性。如果有人日後向類型添加新成 員,但並未意識到它是 SecuritySafeCritical(源於類型級別屬性),就可能導致安全漏洞,這種情況 很可能發生。
盡管類型級別的屬性應用到他們修改的新類型的插槽成員,但不會應用到重寫的成員。如果您使用類 型級別的透明性屬性,在需要時務必專門將屬性添加到重寫成員。
遷移示例
圖 3 是一個在 v2 中編寫的簡單(但不完整)的日志庫。
圖 3 V2 APTCA 庫
using System;
using System.IO;
using System.Security;
using System.Security.Permissions;
// This assembly is meant to be representative of a simple v2 APTCA assembly
// It has some dangerous code protected with demands/link demands
// It exposes some dangerous code in a controlled way with an assert
[assembly: AllowPartiallyTrustedCallers]
public class Logging
{
private string logLocation = @"C:\temp\firstfoo.txt";
public virtual string Usage()
{
return "This is a helpful string";
}
public virtual string LogLocation
{
get
{
return logLocation;
}
[FileIOPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)]
set
{
logLocation = value;
}
}
public virtual void SetLogLocation(int index)
{
switch (index)
{
case 1:
LogLocation = @"C:\temp\foo.txt";
break;
case 2:
LogLocation = @"D:\temp\foo.txt";
break;
case 3:
LogLocation = @"D:\repro\temp\foo.txt";
break;
default:
break;
}
}
public virtual void DeleteLog()
{
FileIOPermission fp = new FileIOPermission(FileIOPermissionAccess.AllAccess, LogLocation);
fp.Assert();
if (File.Exists(LogLocation)) { File.Delete(LogLocation); }
SecurityPermission.RevertAll();
}
// TODO : Put other APIs (creating log, writing to log, etc) here
}
public class OtherLogging : Logging
{
public override string Usage()
{
LogLocation = null;
return "This is a different useful string";
}
// TODO : Put other APIs (creating log, writing to log, etc) here
}
圖 4 顯示的是遷移到 v4 的同一個庫,附帶解釋變化的注釋文字(斜體)。
圖 4 V4 APTCA 庫
using System;
using System.IO;
using System.Security;
using System.Security.Permissions;
// This assembly is meant to be representative of a simple v2 APTCA assembly
// It has some dangerous code protected with demands/link demands
// It exposes some dangerous code in a controlled way with an assert
[assembly: AllowPartiallyTrustedCallers]
public class Logging
{
private string logLocation = @"C:\temp\firstfoo.txt";
// This API can be transparent because it does nothing dangerous.
// Transparent APIs need no attributes because it is the default behavior of a v4
// APTCA assembly
public virtual string Usage()
{
return "This is a helpful string";
}
// Note that transparency attributes do not go directly on properties.
// Instead, they go on the getters and setters (even if the getter and setter
// get the same attributes)
public virtual string LogLocation
{
get
{
return logLocation;
}
// This API is made critical because it sets sensitive data (the path to write to)
// which partial trust code should not be able to do.
[SecurityCritical]
// The previous LinkDemand is removed as the SecurityCritical attribute replaces it
//[FileIOPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)]
set
{
logLocation = value;
}
}
// This API accesses a critical member (LogLocation) and, therefore, cannot be transparent
// However, the access is done in a limited, safe way and we expect transparent code
// should be able to call this API. Therefore, it is SecuritySafeCritical
[SecuritySafeCritical]
public virtual void SetLogLocation(int index)
{
switch (index)
{
case 1:
LogLocation = @"C:\temp\foo.txt";
break;
case 2:
LogLocation = @"D:\temp\foo.txt";
break;
case 3:
LogLocation = @"D:\repro\temp\foo.txt";
break;
default:
break;
}
}
// This API is potentially dangerous; it asserts which means it can’t be transparent
// Because setting LogLocation is protected, however, partial trust code can safely
// call this API. In fact, it is intended that it is safe for partial trust code
// to call this method. Therefore, it is SecuritySafeCritical
[SecuritySafeCritical]
public virtual void DeleteLog()
{
FileIOPermission fp = new FileIOPermission(FileIOPermissionAccess.AllAccess, LogLocation);
fp.Assert();
if (File.Exists(LogLocation)) { File.Delete(LogLocation); }
SecurityPermission.RevertAll();
}
// TODO : Put other APIs (creating log, writing to log, etc) here
}
public class OtherLogging : Logging
{
// The logic for attributing this method is complicated and it is an example of when
// SecAnnotate.exe can be very helpful. This API cannot be transparent because it
// calls a critical member (LogLocation). However, because it overrides a transparent
// method (Usage) it cannot be critical. Therefore, the only possible annotation here
// is SecuritySafeCritical and it is the author’s responsibility to make sure that
// a malicious caller cannot abuse that access.
[SecuritySafeCritical]
public override string Usage()
{
LogLocation = null;
return "This is a different useful string";
}
// TODO : Put other APIs (creating log, writing to log, etc) here
}
同步 CLR 和 Silverlight CoreCLR 安全系統
盡管將 APTCA 和 v4 中的透明性合並似乎很復雜,但合並將最終在部分受信任調用方調用系統敏感資 源時提供直接和有效的保護。此外,此變化較好地協調了桌面 CLR 和 Silverlight CoreCLR 安全系統。
SecAnnotate.exe 等 SDK 工具和 FxCop 規則(可以驗證透明性)有助於簡化遷移。V4 APTCA 程序集 更加易於審核,您只要密切關注 SecuritySafeCritical API(和它們執行的SecurityCritical 調用)即 可對程序集的安全性滿懷信心。
由於透明代碼通常占程序集的80-90% 甚至更多,因此同步合並可顯著減少審核的工作量。如果您希望 更加深入地了解透明性,請參閱 MSDN 文檔以獲得更全面的解釋。