使用 Assert 方法
Assert是一種可在程式碼存取使用權限類別和 PermissionSet 類別上呼叫的方法。 您可以使用 Assert,讓程式碼 (和下游呼叫端) 執行其具有使用權限 (而程式碼的呼叫端可能沒有執行所需的使用權限) 的動作。 安全性判斷提示會變更執行階段在安全性檢查期間所執行的一般程序。 當您判斷提示一個使用權限時,它可以讓安全性系統對已判斷提示的使用權限不檢查其程式碼的呼叫端。
警告 |
---|
使用判斷提示時必須謹慎,因為其可能暴露安全性漏洞,並破壞執行階段強制使用安全性限制的機制。 |
當類別庫呼叫進入 Unmanaged 程式碼內,或執行其使用權限明顯地和類別庫的用途不相關的呼叫時,判斷提示是非常有用的。 例如,所有呼叫進入 Unmanaged 程式碼的 Managed 程式碼都必須有已指定了 UnmanagedCode 旗標的 SecurityPermission。 不是由本機電腦產生的程式碼,如從近端內部網路下載的程式碼,依預設不會被授與這個使用權限。 因此,為了要讓從近端內部網路下載的程式碼可以呼叫使用 Unmanaged 程式碼的程式庫,它必須要有由程式庫判斷提示的使用權限。 此外,某些類別庫也可以執行呼叫端看不到且需要特殊使用權限的呼叫。
當您的程式碼存取完全由呼叫端隱藏的資源時也可以使用判斷提示。 例如,假設您的類別庫要取得資料庫中的資訊,但在取得的過程中仍可讀取電腦登錄中的資訊。 因為使用您類別庫的開發人員沒有存取您的原始碼,所以他們無法知道他們的程式碼需要 RegistryPermission 才能使用您的程式碼。 在這種情形下,如果您決定不必要求您程式碼的呼叫端必須擁有存取登錄的使用權限,您可以判斷提示讀取登錄的使用權限。 類別庫在這種狀態下即適合判斷提示使用權限,使呼叫端不需 RegistryPermission 即可使用該類別庫。
當已判斷提示的使用權限和下層呼叫端所需求的使用權限屬於相同型別,且需求的使用權限是已判斷提示的使用權限子集時,判斷提示才會影響堆疊查核行程。 例如,如果您使用 FileIOPermission 以讀取 C 磁碟機上的所有檔案,且執行 FileIOPermission 的下層需求以讀取 C:\Temp 中的檔案時,判斷提示將會影響堆疊查核行程;然而,當 FileIOPermission 的需求是寫入 C 磁碟機,判斷提示將不會產生任何的影響。
若要執行判斷提示,您的程式碼必須同時被授與您正在判斷提示的使用權限,以及表示執行判斷提示權限的 SecurityPermission。 雖然您可以判斷提示您程式碼尚未被授與的使用權限,但判斷提示將不具任何意義,因為安全性檢查在成功判斷提示之前將會失敗。
下列圖例顯示使用 Assert 時所產生的影響。 假設以下有關組件 A、B、C、E 和 F 以及使用權限 P1 和 P1A 的陳述式 (Statement) 為 True:
P1A 表示讀取 C 磁碟機上 .txt 檔的權限。
P1 表示讀取 C 磁碟機上所有檔案的權限。
P1A 和 P1 都屬於 FileIOPermission 型別,而且 P1A 為 P1 的子集。
組件 E 和 F 已被授與 P1A 使用權限。
組件 C 已被授與 P1 使用權限。
組件 A 和 B 不被授與 P1 或 P1A 使用權限。
方法 A 包含於組件 A、方法 B 包含於組件 B 等等。
使用判斷提示
這個案例中,方法 A 呼叫 B、B 呼叫 C、C 呼叫 E 以及 E 呼叫 F。 方法 C 判斷提示讀取 C 磁碟機上檔案的使用權限 (使用權限 P1),而方法 E 則要求讀取 C 磁碟機上 .txt 檔的使用權限 (使用權限 P1A)。 當在執行階段發現 F 中的需求時,即會執行堆疊查核行程 (Stack Walk) 從 E 開始檢查 F 之所有呼叫端的使用權限。 因為 E 已獲得 P1A 使用權限,因此堆疊查核行程將會進行檢查 C 的使用權限,而找到 C 的判斷提示。 因為需求的使用權限 (P1A) 是已判斷提示的使用權限 (P1) 子集,所以堆疊查核行程將停止並自動繼續進行安全性檢查。 它並不需注意到組件 A 和 B 未被授與使用權限 P1A。 利用判斷提示 P1,方法 C 可以確保它的呼叫端可以存取受 P1 保護的資源,即使呼叫端尚未被授與存取該資源的使用權限。
當您設計一個類別庫且類別可以存取受保護的資源時,在大部分的情況下,您都應該產生安全性要求以要求類別的呼叫端需擁有適當的使用權限。 如果您知道此類別將執行的作業的多數呼叫端都沒有使用權限,而且您願意負責讓這些呼叫端呼叫您的程式碼,則可以在表示程式碼執行作業的使用權限物件上呼叫 Assert 方法,以判斷提示 (Assert) 使用權限。 以這種方式使用 Assert,會讓一般情形下無法使用的呼叫端可以呼叫您的程式碼。 因此,當您在判斷提示一個使用權限時,您應該事先執行適當的安全性檢查以防止您的元件被誤用。
例如,假設一個高度受信任的程式庫類別擁有一個刪除檔案的方法。 它會呼叫一個 Unmanaged Win32 函式來存取檔案。 呼叫端會叫用您程式碼的 Delete 方法,傳入要刪除的檔案名稱,即 C:\Test.txt。 在 Delete 內,您的程式碼會建立 FileIOPermission 物件來表示 C:\Test.txt 的寫入權限 (刪除檔案必須有寫入權限)。然後您的程式碼會呼叫 FileIOPermission 物件的 Demand 方法,以叫用命令式安全性檢查。 如果呼叫堆疊中的其中一個呼叫端沒有擁有這個使用權限,會擲回 SecurityException。 如果沒有擲回例外狀況,即表示所有呼叫端都有存取 C:\Test.txt 的權限。 因為您知道多數呼叫端都沒有存取 Unmanaged 程式碼的權限,所以稍後程式碼會建立 SecurityPermission 物件,表示呼叫 Unmanaged 程式碼和呼叫該物件的 Assert 方法的權限。 最後,它會呼叫 Unmanaged Win32 函式以刪除 C:\Text.txt 並將控制權交回呼叫端。
警告 |
---|
當其他程式碼可以使用您的程式碼,以存取您正在判斷提示 (Assert) 的使用權限所保護的資源時,必須確定您的程式碼中未使用判斷提示 (Assertion)。例如,用來寫入檔案的程式碼,其檔案名稱是由呼叫端指定為參數時,您不可以判斷提示 FileIOPermission 以寫入檔案,因為協力廠商會開啟而誤用您的程式碼. |
當您使用命令式安全性語法時,以相同方法針對多個權限呼叫 Assert 方法時會造成擲回安全性例外狀況 (Exception)。 相反地,您應該建立 PermissionSet 物件,將它傳遞給要叫用的個別使用權限,然後呼叫 PermissionSet 物件上的 Assert 方法。 當使用宣告式的安全性語法時,您可以多次呼叫 Assert 方法。
下列範例說明使用 Assert 方法覆寫安全性檢查的宣告式語法。 請注意,FileIOPermissionAttribute語法採用兩個值:SecurityAction 列舉型別和要授與使用權限的檔案或目錄的位置。 即使尚未檢查呼叫端存取該檔案的權限,呼叫 Assert 會導致需要繼續存取 C:\Log.txt。
[Visual Basic]
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security.Permissions
Namespace LogUtil
Public Class Log
Public Sub New()
End Sub
<FileIOPermission(SecurityAction.Assert, All := "C:\Log.txt")> Public Sub
MakeLog()
Dim TextStream As New StreamWriter("C:\Log.txt")
TextStream.WriteLine("This Log was created on {0}", DateTime.Now) '
TextStream.Close()
End Sub
End Class
End Namespace
namespace LogUtil
{
using System;
using System.IO;
using System.Security.Permissions;
public class Log
{
public Log()
{
}
[FileIOPermission(SecurityAction.Assert, All = @"C:\Log.txt")]
public void MakeLog()
{
StreamWriter TextStream = new StreamWriter(@"C:\Log.txt");
TextStream.WriteLine("This Log was created on {0}", DateTime.Now);
TextStream.Close();
}
}
}
下列程式碼片段說明使用 Assert 方法覆寫安全性檢查的命令式語法。 在這個範例中,將會宣告一個 FileIOPermission 物件的執行個體。 FileIOPermissionAccess.AllAccess 會傳遞給其建構函式 (Constructor),以定義允許存取的型別,後面接著描述檔案位置的字串。 定義 FileIOPermission 物件之後,您只需呼叫它的 Assert 方法,即可覆寫安全性檢查。
[Visual Basic]
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security.Permissions
Namespace LogUtil
Public Class Log
Public Sub New()
End Sub 'New
Public Sub MakeLog()
Dim FilePermission As New FileIOPermission(FileIOPermissionAccess.AllAccess, "C:\Log.txt")
FilePermission.Assert()
Dim TextStream As New StreamWriter("C:\Log.txt")
TextStream.WriteLine("This Log was created on {0}", DateTime.Now)
TextStream.Close()
End Sub
End Class
End Namespace
namespace LogUtil
{
using System;
using System.IO;
using System.Security.Permissions;
public class Log
{
public Log()
{
}
public void MakeLog()
{
FileIOPermission FilePermission = new FileIOPermission(FileIOPermissionAccess.AllAccess,@"C:\Log.txt");
FilePermission.Assert();
StreamWriter TextStream = new StreamWriter(@"C:\Log.txt");
TextStream.WriteLine("This Log was created on {0}", DateTime.Now);
TextStream.Close();
}
}
}