Управление доступом для кода и 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 Security.

Сборки

Сборки представляют собой базовый элемент развертывания, управления версиями, повторного использования, назначения областей активации и прав доступа для приложения .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.

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

Свойство атрибута разрешения Описание
Action Возвращает или задает действие по обеспечению безопасности. Наследуется от SecurityAttribute.
AllowBlankPassword Разрешает или запрещает использование пустого пароля в строке соединения. Допустимые значения - true (чтобы разрешить использование пустых паролей) и false (чтобы запретить использование пустых паролей). Наследуется от DBDataPermissionAttribute.
ConnectionString Определяет допустимую строку соединения. Можно определить несколько строк соединения. Примечание. Не включайте идентификатор пользователя или пароль в строку подключения. В этой версии изменение ограничений строки соединения при помощи средства настройки .NET Framework невозможно.

Наследуется от DBDataPermissionAttribute.
KeyRestrictions Определяет допустимые или недопустимые параметры строки соединения. Параметры строки подключения определяются в форме <parameter name>= . Можно указать несколько параметров, разделив их точкой с запятой (;). Примечание. Если параметр не задан KeyRestrictions , но KeyRestrictionBehavior для свойства задано значение AllowOnly или PreventUsage , дополнительные параметры строки подключения не допускаются. Наследуется от DBDataPermissionAttribute.
KeyRestrictionBehavior Определяет единственно допустимые дополнительные параметры строки соединения (AllowOnly) или определяет недопустимые дополнительные параметры (PreventUsage). Значение по умолчанию — AllowOnly. Наследуется от DBDataPermissionAttribute.
TypeID Возвращает уникальный идентификатор для этого атрибута при реализации в производном классе. Наследуется от Attribute.
Unrestricted Указывает, объявлено ли неограниченное право доступа к ресурсу. Наследуется от SecurityAttribute.

Синтаксис ConnectionString

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

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

Синтаксис KeyRestrictions

В следующем примере включается одна и та же строка подключения, что позволяет Encrypt использовать Packet Size Параметры строки соединения и, однако, обеспечивает использование любых других параметров строки подключения.

<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 разрешения, импортировать набор разрешений CustomLocalIntranet с помощью Caspol.exe (средство политики управления доступом для кода)и установить для набора разрешений значение LocalIntranet_Zone CustomLocalIntranet.

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

Далее приведен образец набора прав доступа для поставщика данных .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 отображается в консольном окне.

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);
         }
      }
   }
}
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

Вот что должно отобразиться в консольном окне:

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 и функции Windows API. При выполнении неуправляемого кода возникают особые вопросы безопасности, с тем чтобы не подвергать риску общую безопасность приложения. Дополнительные сведения см. в разделе Взаимодействие с неуправляемым кодом.

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

См. также