共用方式為


程式碼存取安全性和 ADO.NET

.NET Framework 會提供以角色為基礎的安全性和程式碼存取安全性 (CAS),而這兩種安全性都是使用 Common Language Runtime (CLR) 所提供的通用基礎結構所實作的。 在 Unmanaged 程式碼的作用範圍內,大多數應用程式都是以使用者或主體的權限執行。 因此,當擁有更高權限的使用者執行惡意或充滿錯誤的軟體時,就可能損害電腦系統和竊取私人資料。

相較之下,在 .NET Framework 中執行的 Managed 程式碼具有單獨套用至程式碼的程式碼存取安全性。 系統是否允許執行程式碼會取決於程式碼的來源或程式碼識別的其他層面,而非單獨取決於主體的識別。 這能降低 Managed 程式碼被誤用的可能性。

程式碼存取權限

執行程式碼時,它會顯示 CLR 安全性系統所評估的辨識項。 一般來說,這個辨識項會洩露程式碼的來源,包含 URL、大小和區域,以及用來確保組件識別的數位簽章。

CLR 僅允許程式碼執行該程式碼有權執行的這些作業。 程式碼可以要求權限,而且系統會根據系統管理員所設定的安全性原則來接受這些要求。

注意事項注意事項

CLR 中執行的程式碼不能授與其本身的使用權限。例如,程式碼可以要求而且被授與少於安全性原則允許的權限,但是它絕不會被授與更多權限。授與權限時,系統是以完全沒有權限開始,然後加入執行特定工作的最少權限。如果一開始便使用所有權限,然後再拒絕個別的權限,則會造成應用程式不安全,因為可能會授與超出必要的權限而導致意外安全性漏洞。如需詳細資訊,請參閱設定安全性原則安全性原則管理.

程式碼存取權限有三種類型:

  • Code access permissions衍生自 CodeAccessPermission 類別。 為了存取受保護資源 (例如檔案和環境變數) 以及執行受保護作業 (例如存取 Unmanaged 程式碼),因此需要權限。

  • Identity permissions 代表可識別組件的特性。 系統會根據辨識項 (可能包含數位簽章或程式碼來源等項目),將權限授與組件。 識別權限也衍生自 CodeAccessPermission 基底類別 (Base Class)。

  • Role-based security permissions是以主體是否具有指定的識別或屬於指定角色成員為基礎。 PrincipalPermission 類別允許針對使用中主體進行宣告式和必要的權限檢查。

為了判斷程式碼是否經授權可存取資源或執行某項作業,執行階段的安全性系統會周遊呼叫堆疊,並比較每個呼叫端被授與的權限與要求的權限。 如果呼叫堆疊中的任何呼叫端沒有要求的權限,系統就會擲回 SecurityException 並拒絕存取。

要求權限

要求權限的目的是向執行階段通知您的應用程式需要哪些權限才能執行,以及確保它只會收到實際需要的權限。 例如,如果您的應用程式必須將資料寫入本機磁碟,它就需要 FileIOPermission。 如果系統沒有授與該權限,當此應用程式嘗試寫入磁碟時,它就會失敗。 不過,如果應用程式要求 FileIOPermission,但系統沒有授與該權限,則此應用程式一開始將產生例外狀況而且不會載入。

在應用程式僅需要從磁碟中讀取資料的情況下,您可以要求絕不授與任何寫入權限給應用程式。 在錯誤或惡意攻擊的事件中,您的程式碼無法破壞它所運作的資料。 如需詳細資訊,請參閱要求使用權限

以角色為基礎的安全性和 CAS

同時實作以角色為基礎的安全性和程式碼存取安全性 (CAS) 可強化應用程式的整體安全性。 以角色為基礎的安全性可以根據 Windows 帳戶或自訂識別,將安全性主體的相關資訊提供給目前的執行緒。 此外,應用程式通常會根據使用者所提供的認證,提供對資料或資源的存取。 基本上,這類應用程式會檢查使用者的角色並根據這些角色提供資源存取。

以角色為基礎的安全性可讓某個元件在執行階段識別目前的使用者及其相關聯的角色。 然後,系統會使用 CAS 原則來對應這項資訊,以便判斷在執行階段授與的權限集合。 若為指定的應用程式定義域,主應用程式 (Host) 就可以變更預設的以角色為基礎安全性原則,並且設定預設的安全性主體,以便代表某位使用者以及與該使用相關聯的角色。

CLR 會使用某些權限來實作在 Managed 程式碼上強制執行限制的機制。 以角色為基礎的安全性權限會提供一項機制,讓您探索某位使用者 (或代表使用者運作的代理程式) 是否具有特定識別或屬於指定角色的成員。 如需詳細資訊,請參閱安全性權限

根據您所建立的應用程式類型,您也應該考慮在資料庫中實作以角色為基礎的權限。 如需 SQL Server 中以角色為基礎之安全性的詳細資訊,請參閱 SQL Server 安全性 (ADO.NET)

組件

組件會構成 .NET Framework 應用程式之部署、版本控制、重複使用、啟動範圍和安全性權限的基本單位。 組件會提供針對一起運作而建立而且構成邏輯功能單位之類型和資源的組合。 對 CLR 而言,類型不會存在組件內容外部。 如需建立和部署組件的詳細資訊,請參閱使用組件設計程式

強式命名組件

強式名稱 (Strong Name) 或數位簽章包含組件的識別,其中包括簡單文字名稱、版本號碼和文化特性資訊 (如果有提供的話),以及公開金鑰 (Public Key) 和數位簽章。 數位簽章是從使用對應之私密金鑰的組件檔中產生的。 組件檔包含組件資訊清單 (Assembly Manifest),其中包含組成組件之所有檔案的名稱和雜湊。

強式命名組件可提供應用程式或元件唯一的識別,讓其他軟體用來明確參考該應用程式或元件。 強式命名可保護組件,讓內含惡意程式碼的組件無法假冒該組件。 此外,強式命名還能確保不同元件版本之間的版本一致性。 您必須強式命名將部署到全域組件快取 (GAC) 的組件。 如需詳細資訊,請參閱建立和使用強式名稱的組件

ADO.NET 2.0 中的部分信任

在 ADO.NET 2.0 中,.NET Framework Data Provider for SQL Server、.NET Framework Data Provider for OLE DB、.NET Framework Data Provider for ODBC 和 .NET Framework Data Provider for Oracle 現在都可以在部分信任的環境中執行。 在舊版的 .NET Framework 中,只有 System.Data.SqlClient 才能在低於完全信任的應用程式中使用。

使用 SQL Server 提供者的部分信任應用程式至少必須具有執行和 SqlClientPermission 使用權限。

部分信任的使用權限屬性

在部分信任案例中,可使用 SqlClientPermissionAttribute 成員進一步限制 SQL Server 的 .NET Framework 資料提供者之可用功能。

注意事項注意事項

.NET Framework Data Provider for SQL Server 需要有「完全信任安全性」使用權限,才能在啟用 SQL Server 2000 之 SQL 偵錯的情況下開啟 SqlConnection。SQL Server 2005 的 SQL 偵錯不會使用這個類別。如需詳細資訊,請參閱《SQL Server 2005 線上叢書》。

下列表格列出可用的 SqlClientPermissionAttribute 屬性及其說明:

使用權限屬性

說明

Action

取得或設定安全性動作。 繼承自 SecurityAttribute

AllowBlankPassword

啟用或停用在連接字串中使用空白密碼。 有效值為 true (表示啟用空白密碼) 和 false (表示停用空白密碼)。 繼承自 DBDataPermissionAttribute

ConnectionString

識別允許的連接字串。 可識別多個連接字串。

注意事項注意事項
不要在連接字串中包含使用者 ID 或密碼。在這個發行版本中,您無法使用 .NET Framework 組態工具變更連接字串限制。

繼承自 DBDataPermissionAttribute

KeyRestrictions

識別是否為允許的連接字串參數。 連接字串參數是以格式 <parameter name>= 識別的。 也可以指定多個參數,只要以分號 (;) 將其分隔即可。

注意事項注意事項
如果您沒有指定 KeyRestrictions,卻將 KeyRestrictionBehavior 屬性設為 AllowOnly 或 PreventUsage,則不允許使用其他連接字串參數。繼承自 DBDataPermissionAttribute

KeyRestrictionBehavior

將連接字串參數識別為唯一允許的其他參數 (AllowOnly),或識別不允許的其他參數 (PreventUsage)。 預設值為 AllowOnly。 繼承自 DBDataPermissionAttribute

TypeID

在衍生類別中實作時,取得唯一識別項。 繼承自 Attribute

Unrestricted

指示是否針對資源,宣告不受限的使用權限。 繼承自 SecurityAttribute

ConnectionString 語法

下列範例將示範如何使用組態檔的 connectionStrings 項目,只允許使用特定的連接字串。 如需從組態檔儲存和擷取連接字串的詳細資訊,請參閱 連接字串 (ADO.NET)

<connectionStrings>
  <add name="DatabaseConnection" 
    connectionString="Data Source=(local);Initial 
    Catalog=Northwind;Integrated Security=true;" />
</connectionStrings>

KeyRestrictions 語法

下列範例啟用相同的連接字串,同時也啟用 Encrypt 和 PacketSize 連接字串選項,但限制使用任何其他連接字串選項。

<connectionStrings>
  <add name="DatabaseConnection" 
    connectionString="Data Source=(local);Initial 
    Catalog=Northwind;Integrated Security=true;"
    KeyRestrictions="Encrypt=;Packet Size=;"
    KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>

含 PreventUsage 的 KeyRestrictionBehavior 語法

下列範例將啟用相同的連接字串,並允許 User Id、Password 和 Persist Security Info 以外的其他所有連接參數。

<connectionStrings>
  <add name="DatabaseConnection" 
    connectionString="Data Source=(local);Initial 
    Catalog=Northwind;Integrated Security=true;"
    KeyRestrictions="User Id=;Password=;Persist Security Info=;"
    KeyRestrictionBehavior="PreventUsage" />
</connectionStrings>

含 AllowOnly 的 KeyRestrictionBehavior 語法

下列範例啟用兩個連接字串,這兩個連接字串同時包含 Initial Catalog、Connection Timeout、Encrypt 和 Packet Size 參數。 所有其他的連接字串參數則都限制使用。

<connectionStrings>
  <add name="DatabaseConnection" 
    connectionString="Data Source=(local);Initial 
    Catalog=Northwind;Integrated Security=true;"
    KeyRestrictions="Initial Catalog;Connection Timeout=;
       Encrypt=;Packet Size=;" 
    KeyRestrictionBehavior="AllowOnly" />

  <add name="DatabaseConnection2" 
    connectionString="Data Source=SqlServer2;Initial 
    Catalog=Northwind2;Integrated Security=true;"
    KeyRestrictions="Initial Catalog;Connection Timeout=;
       Encrypt=;Packet Size=;" 
    KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>

以自訂的使用權限集啟用部分信任

若要啟用特定區域的 System.Data.SqlClient 使用權限,系統管理員必須建立自訂的使用權限集合,並將其設定為特定區域的使用權限集合。 不可修改預設的使用權限集合 (例如 LocalIntranet)。 例如,若要併入具有 LocalIntranet 之 Zone 的程式碼的 System.Data.SqlClient 使用權限,系統管理員可以複製 LocalIntranet 的使用權限集合、將它重新命名為 "CustomLocalIntranet"、加入 System.Data.SqlClient 使用權限、使用 Caspol.exe (程式碼存取安全性原則工具) 匯入 CustomLocalIntranet 使用權限集合,然後將 LocalIntranet_Zone 的使用權限集合設為 CustomLocalIntranet。

使用權限集合範例

下列是部分受信任案例中的「SQL Server 的 .NET Framework 資料提供者」使用權限集合範例。 如需建立自訂使用權限集合的詳細資訊,請參閱 使用 Caspol.exe 設定使用權限集合

<PermissionSet class="System.Security.NamedPermissionSet"
  version="1"
  Name="CustomLocalIntranet"
  Description="Custom permission set given to applications on
    the local intranet">

<IPermission class="System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
AllowBlankPassword="False">
<add ConnectionString="Data Source=(local);Integrated Security=true;"
 KeyRestrictions="Initial Catalog=;Connection Timeout=;
   Encrypt=;Packet Size=;" 
 KeyRestrictionBehavior="AllowOnly" />
 </IPermission>
</PermissionSet>

使用安全性使用權限驗證 ADO.NET 程式碼存取

若為部分信任案例,則可指定 SqlClientPermissionAttribute,藉以取得程式碼中之特定方法的 CAS 權限。 如果生效的限制安全性原則不允許該權限,則在執行程式碼前會擲回例外狀況。 如需安全性原則的詳細資訊,請參閱 安全性原則管理安全性原則的最佳作法

範例

下列範例示範如何撰寫需要特定連接字串的程式碼。 它模擬如何拒絕 System.Data.SqlClient 的不受限權限,而系統管理員在實務上會使用 CAS 原則來實作這些權限。

安全性注意事項安全性注意事項

設計 ADO.NET 的 CAS 權限時,最正確的模式是以最大的限制 (完全沒有權限) 開始,然後加入程式碼必須執行之特殊工作所需的特定權限。相反的模式 (以所有權限開始,然後再拒絕特定權限) 並不安全,因為有許多方法可表示相同的連接字串。例如,如果您以所有權限開始,然後嘗試拒絕連接字串 "server=someserver" 的用法,您仍可使用 "server=someserver.mycompany.com" 字串。只要以不授與任何權限開始,您就能減少權限集合具有漏洞的機會。

下列程式碼將示範 SqlClient 如何執行安全性需求,也就是在沒有適當的 CAS 權限時,擲回 SecurityException。 主控台視窗會顯示 SecurityException 輸出。

Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Security
Imports System.Security.Permissions

Namespace PartialTrustTopic
   Public Class PartialTrustHelper
      Inherits MarshalByRefObject
      Public Sub TestConnectionOpen(ByVal connectionString As String)
         ' Try to open a connection.
         Using connection As New SqlConnection(connectionString)
            connection.Open()
         End Using
      End Sub
   End Class

   Class Program
      Public Shared Sub Main(ByVal args As String())
         TestCAS("Data Source=(local);Integrated Security=true", "Data Source=(local);Integrated Security=true;Initial Catalog=Test")
      End Sub

      Public Shared Sub TestCAS(ByVal connectString1 As String, ByVal connectString2 As String)
         ' Create permission set for sandbox AppDomain.
         ' This example only allows execution.
         Dim permissions As New PermissionSet(PermissionState.None)
         permissions.AddPermission(New SecurityPermission(SecurityPermissionFlag.Execution))

         ' Create sandbox AppDomain with permission set that only allows execution,
         ' and has no SqlClientPermissions.
         Dim appDomainSetup As New AppDomainSetup()
         appDomainSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase
         Dim firstDomain As AppDomain = AppDomain.CreateDomain("NoSqlPermissions", Nothing, appDomainSetup, permissions)

         ' Create helper object in sandbox AppDomain so that code can be executed in that AppDomain.
         Dim helperType As Type = GetType(PartialTrustHelper)
         Dim firstHelper As PartialTrustHelper = DirectCast(firstDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName), PartialTrustHelper)

         Try
            ' Attempt to open a connection in the sandbox AppDomain.
            ' This is expected to fail.
            firstHelper.TestConnectionOpen(connectString1)
            Console.WriteLine("Connection opened, unexpected.")
         Catch ex As System.Security.SecurityException

            ' Uncomment the following line to see Exception details.
            ' Console.WriteLine("BaseException: " + ex.GetBaseException());
            Console.WriteLine("Failed, as expected: {0}", ex.FirstPermissionThatFailed)
         End Try

         ' Add permission for a specific connection string.
         Dim sqlPermission As New SqlClientPermission(PermissionState.None)
         sqlPermission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly)

         permissions.AddPermission(sqlPermission)

         Dim secondDomain As AppDomain = AppDomain.CreateDomain("OneSqlPermission", Nothing, appDomainSetup, permissions)
         Dim secondHelper As PartialTrustHelper = DirectCast(secondDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName), PartialTrustHelper)

         ' Try connection open again, it should succeed now.
         Try
            secondHelper.TestConnectionOpen(connectString1)
            Console.WriteLine("Connection opened, as expected.")
         Catch ex As System.Security.SecurityException
            Console.WriteLine("Unexpected failure: {0}", ex.Message)
         End Try

         ' Try a different connection string. This should fail.
         Try
            secondHelper.TestConnectionOpen(connectString2)
            Console.WriteLine("Connection opened, unexpected.")
         Catch ex As System.Security.SecurityException
            Console.WriteLine("Failed, as expected: {0}", ex.Message)
         End Try
      End Sub
   End Class
End Namespace
using System;
using System.Data;
using System.Data.SqlClient;
using System.Security;
using System.Security.Permissions;

namespace PartialTrustTopic {
   public class PartialTrustHelper : MarshalByRefObject {
      public void TestConnectionOpen(string connectionString) {
         // Try to open a connection.
         using (SqlConnection connection = new SqlConnection(connectionString)) {
            connection.Open();
         }
      }
   }

   class Program {
      static void Main(string[] args) {
         TestCAS("Data Source=(local);Integrated Security=true", "Data Source=(local);Integrated Security=true;Initial Catalog=Test");
      }

      static void TestCAS(string connectString1, string connectString2) {
         // Create permission set for sandbox AppDomain.
         // This example only allows execution.
         PermissionSet permissions = new PermissionSet(PermissionState.None);
         permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

         // Create sandbox AppDomain with permission set that only allows execution,
         // and has no SqlClientPermissions.
         AppDomainSetup appDomainSetup = new AppDomainSetup();
         appDomainSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
         AppDomain firstDomain = AppDomain.CreateDomain("NoSqlPermissions", null, appDomainSetup, permissions);

         // Create helper object in sandbox AppDomain so that code can be executed in that AppDomain.
         Type helperType = typeof(PartialTrustHelper);
         PartialTrustHelper firstHelper = (PartialTrustHelper)firstDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName);

         try {
            // Attempt to open a connection in the sandbox AppDomain.
            // This is expected to fail.
            firstHelper.TestConnectionOpen(connectString1);
            Console.WriteLine("Connection opened, unexpected.");
         }
         catch (System.Security.SecurityException ex) {
            Console.WriteLine("Failed, as expected: {0}",
                ex.FirstPermissionThatFailed);

            // Uncomment the following line to see Exception details.
            // Console.WriteLine("BaseException: " + ex.GetBaseException());
         }

         // Add permission for a specific connection string.
         SqlClientPermission sqlPermission = new SqlClientPermission(PermissionState.None);
         sqlPermission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly);

         permissions.AddPermission(sqlPermission);

         AppDomain secondDomain = AppDomain.CreateDomain("OneSqlPermission", null, appDomainSetup, permissions);
         PartialTrustHelper secondHelper = (PartialTrustHelper)secondDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName);

         // Try connection open again, it should succeed now.
         try {
            secondHelper.TestConnectionOpen(connectString1);
            Console.WriteLine("Connection opened, as expected.");
         }
         catch (System.Security.SecurityException ex) {
            Console.WriteLine("Unexpected failure: {0}", ex.Message);
         }

         // Try a different connection string. This should fail.
         try {
            secondHelper.TestConnectionOpen(connectString2);
            Console.WriteLine("Connection opened, unexpected.");
         }
         catch (System.Security.SecurityException ex) {
            Console.WriteLine("Failed, as expected: {0}", ex.Message);
         }
      }
   }
}

您應該在 [主控台] 視窗中看到以下輸出:

Failed, as expected: <IPermission class="System.Data.SqlClient.
SqlClientPermission, System.Data, Version=2.0.0.0, 
  Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1"
  AllowBlankPassword="False">
<add ConnectionString="Data Source=(local);Initial Catalog=
  Northwind;Integrated Security=SSPI" KeyRestrictions=""
KeyRestrictionBehavior="AllowOnly"/>
</IPermission>

Connection opened, as expected.
Failed, as expected: Request failed.

與 Unmanaged 程式碼的互通性

在 CLR 外部執行的程式碼稱為 Unmanaged 程式碼。 因此,CAS 等安全性機制無法套用至 Unmanaged 程式碼。 COM 元件、ActiveX 介面及 Win32 API 函式都是 Unmanaged 程式碼的範例。 執行 Unmanaged 程式碼時,您應該套用特殊安全性考量,以免危及整體應用程式安全性。 如需詳細資訊,請參閱與 Unmanaged 程式碼互通

.NET Framework 也透過 COM Interop 提供存取,藉以支援現有 COM 元件的回溯相容性 (Backward Compatibility)。 您可以使用 COM Interop 工具來匯入相關的 COM 型別,以便將 COM 元件併入 .NET Framework 應用程式中。 一旦匯入之後,COM 型別便可供使用。 COM Interop 也會將組件中繼資料匯出至型別程式庫並將 Managed 元件註冊為 COM 元件,藉以讓 COM 用戶端存取 Managed 程式碼。 如需詳細資訊,請參閱進階 COM 互通性

請參閱

其他資源

保護 ADO.NET 應用程式

機器碼和 .NET Framework 程式碼中的安全性

Code Access Security

Role-Based Security