Поделиться через


Управление доступом для кода и ADO.NET

Платформа .NET Framework предлагает безопасность на основе ролей, а также управление доступом для кода (CAS). Оба средства реализуются при помощи общей инфраструктуры, предоставленной средой CLR. В области неуправляемого кода большинство приложений выполняются с правами доступа пользователя или участника. В результате, когда вредоносное программное обеспечение или программы с ошибками запускаются пользователем с повышенными правами доступа, компьютерные системы могут повреждаться, а личные данные — похищаться.

В отличие от этого выполнение управляемого кода в платформе .NET Framework включает управление доступом для кода, которое применяется только к коду. Разрешено ли выполнение этого кода или нет, зависит от источника кода или других аспектов происхождения кода, а не только идентификатора участника. Это снижает вероятность неправильного использования управляемого кода.

Права доступа для кода

При выполнении кода он предоставляет свидетельство, которое проверяется системой безопасности CLR. Обычно это свидетельство состоит из источника кода, включая URL-адрес, узел и зону, а также цифровые сигнатуры, которые подтверждают происхождение сборки.

Среда CLR позволяет коду выполнять только те операции, на которые у него есть права. Код может запросить права доступа, и эти запросы удовлетворяются, исходя из политики безопасности, заданной администратором.

ПримечаниеПримечание

Код, выполняющийся в среде CLR, не может предоставлять права доступа самому себе.Например, код может запросить и предоставить меньшие права доступа, чем позволено политикой безопасности, при этом он никогда не получить больше прав доступа.При предоставлении прав доступа следует начинать с нуля и добавлять минимальные необходимые права для каждой выполняемой задачи.Если начать с предоставления всех прав, а затем удалять отдельные их них, то приложение окажется небезопасным. В нем могут возникать неожиданные бреши в системе безопасности из-за предоставления больших прав доступа, чем необходимо.Дополнительные сведения см. в разделах Настройка политики безопасности и Управление политикой безопасности.

Существует три типа прав доступа для кода.

  • Code access permissions получаются из класса CodeAccessPermission. Права доступа требуются для использования защищенных ресурсов, например файлов и переменных среды, а также для выполнения защищенных операций, например для доступа к неуправляемому коду.

  • Identity permissions представляют собой характеристики, определяющие сборку. Права доступа предоставляются сборке, исходя из свидетельства, которое может содержать такие элементы, как цифровая сигнатура или сведения об источнике кода. Права доступа для удостоверения также являются производными от базового класса CodeAccessPermission.

  • Role-based security permissions основаны на том, имеет ли участник указанное свидетельство или является ли членом указанной роли. Класс PrincipalPermission позволяет проводить декларативные и императивные проверки прав доступа по активному участнику.

Система безопасности среды выполнения просматривает стек вызова, сравнивая разрешения, предоставленные каждому вызывающему, с запрашиваемым разрешением, чтобы определить, разрешено ли коду иметь доступ к ресурсу. Если какой-либо вызывающий объект в стеке вызова не имеет запрашиваемого разрешения, формируется исключение SecurityException и ему отказывается в доступе.

Запрос прав доступа

Цель запроса прав доступа состоит в том, чтобы проинформировать среду выполнения о том, какие права доступа требуются данному приложению для работы, а также чтобы обеспечить получение им только тех прав доступа, которые ему действительно нужны. Например, если приложению требуется записать данные на локальный диск, ему необходимо право доступа FileIOPermission. Если это право доступа не было предоставлено, при попытке приложения записать данные на диск произойдет сбой. Однако если приложение запрашивает право доступа FileIOPermission и не получает его, то в результате оно формирует исключение и не загружается.

В том случае, когда приложению требуется только считать данные с диска, можно потребовать, чтобы ему никогда не предоставлялись права доступа для записи. При возникновении ошибки или атаки злоумышленника код не сможет повредить данные, с которыми он работает. Дополнительные сведения см. в разделе Запрос разрешений.

Безопасность на основе ролей и CAS

Реализация безопасности на основе ролей и управления доступом для кода (CAS) повышает общую безопасность приложения. Безопасность на основе ролей может основываться на учетных записях Windows или пользовательских удостоверениях, которые предоставляют сведения об участнике безопасности текущему потоку. Кроме того, часто требуется, чтобы приложения предоставляли доступ к данным или ресурсам, исходя из учетных данных, переданных пользователем. Такие приложения обычно проверяют роль пользователя и предоставляют доступ к ресурсам на основании этих ролей.

Безопасность на основе ролей позволяет компоненту идентифицировать текущих пользователей и их роли во время выполнения. Затем эти сведения сопоставляются при помощи политики CAS, чтобы определить набор прав доступа, предоставляемых во время выполнения. Хост-приложение может изменить политику безопасности на основе ролей по умолчанию и задать участника безопасности по умолчанию, который представляет пользователя и роли, связанные с этим пользователем, для указанного домена приложения.

В среде CLR для реализации своего механизма применения ограничений для управляемого кода используются права доступа. Права доступа на основе ролей предоставляют механизм для определения, имеет ли пользователь (или агент, действующий от имени пользователя) конкретное удостоверение или является ли он членом указанной роли. Дополнительные сведения см. в разделе Разрешения безопасности.

В зависимости от типа создаваемого приложения также следует рассмотреть возможность реализации в базе данных прав доступа на основе ролей. Дополнительные сведения о безопасности на основе ролей в SQL Server см. в разделе Безопасность SQL Server (ADO.NET).

Сборки

Сборки представляют собой базовый элемент развертывания, управления версиями, повторного использования, назначения областей активации и прав доступа для приложения .NET Framework. Сборка предоставляет коллекцию типов и ресурсов, которые предназначены для совместной работы и формируют логическую единицу функциональности. В среде CLR типы не существуют вне контекста сборки. Дополнительные сведения о создании и развертывании сборок см. в разделе Программирование с использованием сборок.

Назначение строгих имен сборкам

Строгое имя или цифровая сигнатура состоит из идентификатора сборки, в который входит ее простое текстовое имя, номер версии и сведения о языке и региональных настройках (если указано) плюс открытый ключ и цифровая сигнатура. Цифровая сигнатура формируется из файла сборки при помощи соответствующего закрытого ключа. В файле сборки содержится манифест сборки, в котором указаны имена и хэши всех файлов, составляющих эту сборку.

Строгое имя сборки дает приложению или компоненту уникальный идентификатор, который может использоваться другими программами явной ссылки на сборку. Строгие имена предотвращают имитацию сборок со стороны сборок, содержащих вредоносный код. Строгие имена также обеспечивают согласованность разных версий компонента. Сборки, которые будут установлены в глобальный кэш сборок, должны получать строгие имена. Дополнительные сведения см. в разделе Создание и использование сборок со строгими именами.

Частичный уровень доверия в ADO.NET 2.0

Теперь в ADO.NET 2.0 поставщик данных .NET Framework для SQL Server, поставщик данных .NET Framework для OLE DB, поставщик данных .NET Framework для ODBC и поставщик данных .NET Framework для Oracle могут работать в частично доверенных средах. В предыдущих версиях платформы .NET Framework в приложениях с неполным доверием поддерживался только клиент System.Data.SqlClient.

У частично доверенного приложения, использующего поставщик the SQL Server, как минимум, должно быть право доступа для выполнения и право доступа SqlClientPermission.

Свойства атрибута разрешения для частичного уровня доверия

Чтобы еще сильнее ограничить возможности поставщика данных .NET Framework для SQL Server, в сценариях с частичным уровнем доверия можно использовать члены SqlClientPermissionAttribute.

ПримечаниеПримечание

Поставщику данных .NET Framework для SQL Server для открытия соединения SqlConnection с отладкой SQL требуется предоставление в SQL Server 2000 уровня безопасности «Полное доверие».SQL Debugging для SQL Server 2005 не использует этот класс.Подробные сведения см. в электронной документации по SQL Server 2005.

В следующей таблице содержится список имеющихся свойств SqlClientPermissionAttribute и их описания.

Свойство атрибута разрешения

Описание

Action

Возвращает или задает действие по обеспечению безопасности. Наследуется от SecurityAttribute.

AllowBlankPassword

Разрешает или запрещает использование пустого пароля в строке соединения. Допустимые значения — true (чтобы разрешить использование пустых паролей) и false (чтобы запретить использование пустых паролей). Наследуется от DBDataPermissionAttribute.

ConnectionString

Определяет допустимую строку соединения. Можно определить несколько строк соединения.

ПримечаниеПримечание
Не включайте в строку соединения идентификатор пользователя или пароль.В этой версии изменение ограничений строки соединения при помощи средства настройки .NET Framework невозможно.

Наследуется от DBDataPermissionAttribute.

KeyRestrictions

Определяет допустимые или недопустимые параметры строки соединения. Параметры строки соединения определяются в виде <имя параметра>=. Можно указать несколько параметров, разделив их точкой с запятой (;).

ПримечаниеПримечание
Если не указать 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>

Синтаксис KeyRestrictionBehavior с PreventUsage

В следующем примере включается та же строка соединения и разрешается использование всех остальных параметров строки соединения, за исключением 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>

Синтаксис KeyRestrictionBehavior с AllowOnly

В следующем примере включаются две строки соединения, которые также содержат параметры 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, изменять нельзя. Например, чтобы включить права доступа System.Data.SqlClient для кода, имеющего зону Zone, равную LocalIntranet, системный администратор может скопировать набор прав доступа для LocalIntranet, переименовать его в «CustomLocalIntranet», добавить права доступа System.Data.SqlClient, импортировать набор прав доступа с помощью Caspol.exe (средство настройки политики управления доступом для кода) и установить CustomLocalIntranet в качестве набора прав доступа для зоны LocalIntranet_Zone.

Образец набора прав доступа

Далее приведен образец набора прав доступа для поставщика данных .NET Framework для SQL Server в частично доверенном сценарии. Сведения о создании пользовательских наборов прав доступа см. в разделе Настройка наборов разрешений с помощью 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 при помощи прав доступа Security Permissions

В сценариях с частичным доверием для некоторых методов в коде можно затребовать права доступа CAS, указав SqlClientPermissionAttribute. Если предоставление этого права доступа запрещено действующей политикой безопасности, перед выполнением кода формируется исключение. Дополнительные сведения о политике безопасности см. в разделе Управление политикой безопасности и Лучшие методики для политики безопасности.

Пример

В следующем примере демонстрируется, как написать код, в котором требуется определенная строка соединения. В нем имитируется запрет на неограниченные права доступа для System.Data.SqlClient, который в реальной среде реализуется системным администратором при помощи политики CAS.

Примечание о безопасностиПримечание по безопасности

При разработке прав доступа CAS для ADO.NET рекомендуется начинать с наиболее жестких ограничений (полное отсутствие прав доступа), а затем добавлять определенные права доступа, необходимые конкретной задаче, которую требуется выполнить коду.Противоположный подход, когда начинают с предоставления всех прав доступа, а затем удаляют определенные права, не является безопасным, поскольку существует много способов выражения одинаковой строки соединения.Например, если начать с предоставления всех прав доступа, а затем попытаться запретить использование строки соединения «server=someserver», то строка «server=someserver.mycompany.com» останется допустимой.Начиная с отсутствия предоставленных прав, можно сократить вероятность появления уязвимостей, связанных с набором прав доступа.

В следующем коде демонстрируется, как SqlClient выполняет запрос на право доступа, который формирует исключение SecurityException при отсутствии соответствующих прав доступа CAS. Исключение 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.

Совместимость с неуправляемым кодом

Код, который выполняется вне среды CLR, называется неуправляемым. Поэтому применять к неуправляемому коду такие механизмы безопасности, как CAS, нельзя. Примерами неуправляемого кода являются компоненты COM, интерфейсы ActiveX и функции Win32 API. При выполнении неуправляемого кода возникают особые вопросы безопасности, с тем чтобы не подвергать риску общую безопасность приложения. Дополнительные сведения см. в разделе Взаимодействие с неуправляемым кодом.

Платформа .NET Framework также поддерживает обратную совместимость с существующими COM-компонентами, предоставляя доступ через COM-взаимодействие. COM-компоненты можно встраивать в приложение .NET Framework, используя средства COM-взаимодействия для импорта соответствующих типов COM. Типы COM готовы к использованию сразу после импорта. COM-взаимодействие также позволяет клиентам COM получать доступ к управляемому коду путем экспорта метаданных сборки в библиотеку типов и регистрации управляемого компонента в виде COM-компонента. Дополнительные сведения см. в разделе Расширенное COM-взаимодействие.

См. также

Другие ресурсы

Защита приложений ADO.NET

Безопасность в машинном коде и коде .NET Framework

Code Access Security

Role-Based Security