Пошаговое руководство. Выпуск кода в сценариях частичного доверия

Обновлен: Ноябрь 2007

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

Bb384237.alert_note(ru-ru,VS.90).gifПримечание.

В версиях, предшествовавших .NET Framework, версия 3.5, для выпуска кода требовалось разрешение ReflectionPermission с флагом ReflectionPermissionFlag.ReflectionEmit. Это разрешение по умолчанию включено в именованные наборы разрешений FullTrust и Intranet, но отсутствует в наборе разрешений сети Internet. Поэтому библиотека может использоваться только при частичном доверии, если она содержит атрибут SecurityCriticalAttribute, а также выполняет метод Assert для ReflectionEmit. Подобные библиотеки нуждаются в тщательном обеспечении безопасности, так как ошибки в коде могут привести к серьезным брешам в системе безопасности. В платформе .NET Framework 3.5 код может быть выпущен в сценариях частичного доверия без создания любых требований к безопасности, так как создание кода по существу не является привилегированной операцией. То есть созданный код имеет не больше разрешений, чем выпустившая его сборка. Это позволяет библиотекам выпускать код, прозрачный для безопасности, и устраняет необходимость в подтверждении ReflectionEmit, что упрощает задачу написания безопасной библиотеки, так как тщательная проработка безопасности будет не нужна.

В этом пошаговом руководстве представлены следующие задачи:

  • Настройка сред с частичным доверием для проверки кода.

  • Выполнение кода в доменах приложений с частичным доверием.

  • Использование анонимно размещенных динамических методов для выпуска и выполнения кода при частичном доверии.

Дополнительные сведения о выпуске кода в сценариях с частичным доверием см. в разделе Вопросы безопасности в порождаемом отражении.

Полный код, показанный в данных процедурах, см. в разделе Пример в конце этого руководства.

Настройка расположений с частичным доверием

В следующих процедурах показано, как настроить расположения, в которых можно выполнить код с частичным доверием.

  • В первой процедуре показано, как создать домен изолированного приложения, в котором код выполняется с интернет-доверием. Также здесь объясняются связанные с этим сложности.

  • Во второй процедуре демонстрируется, как добавить ReflectionPermission с флагом ReflectionPermissionFlag.RestrictedMemberAccess в домен приложения с частичным доверием для разрешения доступа к конфиденциальным данным в сборках с равным или меньшим доверием.

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

Помимо этих процедур в простых сценариях можно использовать атрибут сборки, аналогичный приведенному ниже, для выполнения сборки без разрешения SkipVerification. Аналогичным образом можно использовать атрибут для запрета разрешения MemberAccess.

<Assembly:SecurityPermissionAttribute(SecurityAction.RequestRefuse, Flags:=SecurityPermissionFlag.SkipVerification)>
[assembly:SecurityPermissionAttribute(SecurityAction.RequestRefuse, Flags=SecurityPermissionFlag.SkipVerification)]

Создание изолированных доменов приложений

Чтобы создать домен приложения, в котором сборки будут работать при частичном доверии, необходимо указать набор разрешений, которые должны быть предоставлены сборкам, посредством использования перегрузки метода AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) для создания домена приложения. Самым простым способом указания набора разрешений является получение именованного набора разрешений из политики безопасности.

Bb384237.alert_caution(ru-ru,VS.90).gifВнимание!

Невозможно создать изолированный домен приложения путем указания только свидетельства. Необходимо указать набор прав или уровень политики домена приложения. (В этом разделе не обсуждается настройка уровня политики домена приложения.) Например, при использовании перегрузки метода CreateDomain(String, Evidence) с интернет-свидетельством, разрешения применяются только на границе домена приложения. В самом домене приложения сборкам предоставляются разрешения в зависимости от стандартной политики безопасности. Для приложения консоли на компьютере с полным доверием.

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

Чтобы создать домен приложения с частичным уровнем доверия

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

    Private Shared Function GetNamedPermissionSet(ByVal name As String) As PermissionSet 
    
        If (String.IsNullOrEmpty(name)) Then 
            Throw New ArgumentException("name", "Cannot search for a permission set without a name.")
        End If
    
        Dim foundName As Boolean = False
        Dim setIntersection As New PermissionSet(PermissionState.Unrestricted)
    
        ' Search all policy levels.
        Dim levelEnumerator As IEnumerator = SecurityManager.PolicyHierarchy()
        While (levelEnumerator.MoveNext())
    
            Dim level As PolicyLevel = levelEnumerator.Current 
            Debug.Assert(level IsNot Nothing)
    
            ' If this policy level has a named permission set with the 
            ' specified name, intersect it with previous levels.
            Dim levelSet As PermissionSet = level.GetNamedPermissionSet(name)
            If (levelSet IsNot Nothing) Then
    
                foundName = True
                setIntersection = setIntersection.Intersect(levelSet)
    
                ' Intersect() returns null for an empty set. If this occurs
                ' at any point, the resulting permission set is empty.
                If (setIntersection Is Nothing) Then
                    Return New PermissionSet(PermissionState.None)
                End If
            End If
        End While
    
        If Not foundName Then
            setIntersection = New PermissionSet(PermissionState.None)
        End If
        Return setIntersection
    
    End Function 
    
    private static PermissionSet GetNamedPermissionSet(string name)
    {
        if (String.IsNullOrEmpty(name))
            throw new ArgumentException("name", "Cannot search for a permission set without a name.");
    
        bool foundName = false;
        PermissionSet setIntersection = new PermissionSet(PermissionState.Unrestricted);
    
        // Search all policy levels.
        IEnumerator levelEnumerator = SecurityManager.PolicyHierarchy();
        while (levelEnumerator.MoveNext())
        {
            PolicyLevel level = levelEnumerator.Current as PolicyLevel;
            Debug.Assert(level != null);
    
            // If this policy level has a named permission set with the 
            // specified name, intersect it with previous levels.
            PermissionSet levelSet = level.GetNamedPermissionSet(name);
            if (levelSet != null)
            {
                foundName = true;
                setIntersection = setIntersection.Intersect(levelSet);
    
                // Intersect() returns null for an empty set. If this occurs
                // at any point, the resulting permission set is empty.
                if (setIntersection == null)
                    return new PermissionSet(PermissionState.None);
            }
        }
    
        if (!foundName)
            setIntersection = new PermissionSet(PermissionState.None);
    
        return setIntersection;
    }
    

    Набор прав — это пересечение наборов разрешений, предоставленных на всех уровнях политики. То есть определенное разрешение не предоставляется, если оно не предоставлено на всех уровнях политики. Поэтому вспомогательная функция запускается с набором прав для полного доверия и происходит перечисление иерархии политики, беря за основу пересечение этого набора прав с набором разрешений, определенным для каждого уровня.

    Bb384237.alert_note(ru-ru,VS.90).gifПримечание.

    Можно создать наборы прав, которые будут содержать любую комбинацию разрешений посредством использования класса PermissionSet.

    Использование вспомогательной функции приводится ниже в этой процедуре.

  2. Создайте свидетельство для расположения с частичным довериям с помощью зон безопасности. В этом случае используется интернет-зона.

    Dim zoneEvidence() As Object = { New Zone(SecurityZone.Internet) }
    Dim internetZone As New Evidence(zoneEvidence, zoneEvidence)
    
    Object[] zoneEvidence = { new Zone(SecurityZone.Internet) };
    Evidence internetZone = new Evidence(zoneEvidence, zoneEvidence);
    
  3. Создайте объект AppDomainSetup для инициализации домена приложения с путем приложения. В этом примере кода используется текущая папка.

    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
  4. Используйте вспомогательную функцию для получения именованного набора разрешений из системной политики.

    Dim internetSet As PermissionSet = GetNamedPermissionSet("Internet")
    
    PermissionSet internetSet = GetNamedPermissionSet("Internet");
    
  5. Создайте домен приложения, указав свидетельство, сведения настройки домена приложения и набор правил.

    Dim ad As AppDomain = AppDomain.CreateDomain("ChildDomain1", _
                                                 internetZone, _
                                                 adSetup, _
                                                 internetSet, _
                                                 Nothing)
    
    AppDomain ad = AppDomain.CreateDomain("ChildDomain1", 
                                          internetZone, 
                                          adSetup, 
                                          internetSet, 
                                          null);
    

    С помощью последнего параметра перегрузки метода AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) можно указать набор сборок, которым будет предоставлено полное доверия, а не набор правил домена приложения. Нет необходимости указывать сборки .NET Framework, используемые приложением, так как эти сборки находятся в глобальном кэше сборок. Сборки, находящиеся в глобальном кэше сборок, всегда имеют полное доверие. Этот параметр можно использовать для указания сборок со строгими именами, которые не находятся в глобальном кэше сборок.

Добавление доступа RestrictedMemberAccess к изолированным доменам

Ведущие приложения могут разрешать доступ анонимно размещенных динамических методов к конфиденциальным данным в сборках, уровень доверия которых не превышает уровень доверия сборки, выпускающей код. Чтобы разрешить эту ограниченную возможность пропуска проверок видимости, выполняемых JIT-компилятором, ведущее приложение добавляет объект ReflectionPermission с флагом ReflectionPermissionFlag.RestrictedMemberAccess (RMA) в набор правил.

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

Bb384237.alert_note(ru-ru,VS.90).gifПримечание.

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

Чтобы создать домен приложения с частичным доверием и RMA

  1. Используйте вспомогательную функцию для получения набора разрешений с именем Internet из политики безопасности.

    internetSet = GetNamedPermissionSet("Internet")
    
    internetSet = GetNamedPermissionSet("Internet");
    
  2. Создайте новый объект ReflectionPermission с флагом RestrictedMemberAccess и используйте метод PermissionSet.SetPermission для добавления разрешения в набор правил.

    internetSet.SetPermission( _
        New ReflectionPermission( _
            ReflectionPermissionFlag.RestrictedMemberAccess))
    
    internetSet.SetPermission(
        new ReflectionPermission(
            ReflectionPermissionFlag.RestrictedMemberAccess));
    

    Метод AddPermission добавляет это разрешение в набор правил, если его там еще нет. Если разрешение уже включено в набор правил, указанные флаги добавляются в существующее разрешение.

    Bb384237.alert_note(ru-ru,VS.90).gifПримечание.

    RMA — это функциональная возможность анонимно размещенных динамических методов. Если обычные динамические методы пропускают проверки видимости, выполняемые JIT-компилятором, выпущенный код должен получить разрешение ReflectionPermission с флагом ReflectionPermissionFlag.MemberAccess в добавление к флагу RestrictedMemberAccess.

  3. Создайте домен приложения, указав свидетельство, сведения настройки домена приложения и набор правил.

    ad = AppDomain.CreateDomain("ChildDomain2", _
                                internetZone, _
                                adSetup, _
                                internetSet, _
                                Nothing)
    
    ad = AppDomain.CreateDomain("ChildDomain2", 
                                internetZone, 
                                adSetup, 
                                internetSet, 
                                null);
    

Создание папки с ограниченными разрешениями

В следующей процедуре показано, как создать группу кода, которая будет связывать интернет-разрешения с папкой, а также как добавить флаг RestrictedMemberAccess в набор правил для кода, выполняемого из папки.

Чтобы создать папку с интернет-разрешениями

  1. Нажмите кнопку Пуск и последовательно выберите Панель управления, Администрирование и Конфигурация Microsoft .NET Framework 3.5. Чтобы использовать средство конфигурации, необходимо обладать правами системного администратора.

  2. В левой части окна под заголовком Конфигурация .NET Framework 2.0 последовательно разверните узлы Мой компьютер, Политика безопасности среды выполнения, Компьютер, Группы кода и All_Code.

  3. В правой части окна щелкните Добавить дочернюю группу кода для запуска мастера создания группы кода.

  4. Укажите имя группы кода, например "Изолированная интернет-группа". Нажмите кнопку Далее.

  5. В списке Выберите тип условия для этой группы щелкните URL-адрес. В поле URL-адрес введите полный путь к папке, которую следует использовать, затем нажмите кнопку Далее. Например, допускается ввод адреса следующего вида:

    file://c:/InternetSandbox/*
    
  6. В списке Использовать существующий набор разрешений выберите Интернет и нажмите кнопку Далее.

    Bb384237.alert_note(ru-ru,VS.90).gifПримечание.

    Чтобы указать именованный набор разрешений и объект ReflectionPermission с флагом RestrictedMemberAccess, щелкните команду Создать новый набор разрешений и укажите пользовательский набор разрешений с помощью XML-файла.

  7. Чтобы создать группу кода, нажмите кнопку Готово.

  8. Разместите сборки, которые следует выполнить при частичном доверии, в папку, указанную на этапе 5.

Выполнение кода в изолированных доменах приложения

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

Чтобы определить и выполнить метод в домене приложения

  1. Определите класс, производный от класса MarshalByRefObject. Это позволит создать экземпляры класса в других доменах приложения, а также вызывать методы за пределами домена приложения. Класс в этом примере назван Worker.

    Public Class Worker
        Inherits MarshalByRefObject
    
    public class Worker : MarshalByRefObject
    {
    
  2. Определите открытый метод, содержащий код, который следует выполнить. В этом примере в коде выпускается простой динамический метод, создается делегат для выполнения этого метода и вызывается делегат.

    Public Sub SimpleEmitDemo()
    
        Dim meth As DynamicMethod = new DynamicMethod("", Nothing, Nothing)
        Dim il As ILGenerator = meth.GetILGenerator()
        il.EmitWriteLine("Hello, World!")
        il.Emit(OpCodes.Ret)
    
        Dim t1 As Test1 = CType(meth.CreateDelegate(GetType(Test1)), Test1)
        t1()
    End Sub
    
    public void SimpleEmitDemo()
    {
        DynamicMethod meth = new DynamicMethod("", null, null);
        ILGenerator il = meth.GetILGenerator();
        il.EmitWriteLine("Hello, World!");
        il.Emit(OpCodes.Ret);
    
        Test1 t1 = (Test1) meth.CreateDelegate(typeof(Test1));
        t1();
    }
    
  3. В основной программе обратите внимание на отображаемое имя сборки. Это имя используется при создании экземпляров класса Worker в изолированном домене приложения.

    Dim asmName As String = [Assembly].GetExecutingAssembly().FullName
    
    String asmName = Assembly.GetExecutingAssembly().FullName;
    
  4. В основной программе создайте изолированный домен приложения, как описано в первой процедуре этого пошагового руководства. Нет необходимости добавлять любые разрешения в набор разрешений Internet, так как метод SimpleEmitDemo использует только открытые методы.

  5. В основной программе создайте экземпляр класса Worker в изолированном домене приложения.

    Dim w As Worker = _
        CType(ad.CreateInstanceAndUnwrap(asmName, "Worker"), Worker)
    
    Worker w = (Worker) ad.CreateInstanceAndUnwrap(asmName, "Worker");
    

    Метод CreateInstanceAndUnwrap создает объект в целевом домене приложения и возвращает прокси, который может использоваться для вызова свойств и методов объекта.

    Bb384237.alert_note(ru-ru,VS.90).gifПримечание.

    При использовании этого кода в Visual Studio необходимо изменить имя класса для включения пространства имен. По умолчанию пространство имен носит имя проекта. Например, если проект называется "PartialTrust", именем класса должно являться "PartialTrust.Worker".

  6. Добавьте код для вызова метода SimpleEmitDemo. Вызов маршалируется за пределы домена приложения, а код выполняется в изолированном домене приложения.

    w.SimpleEmitDemo()
    
    w.SimpleEmitDemo();
    

Использование анонимно размещенных динамических методов

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

Bb384237.alert_note(ru-ru,VS.90).gifПримечание.

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

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

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

Чтобы использовать анонимно размещенные динамические методы

  • Создайте анонимно размещенный динамический метод путем использования конструктора, который не указывает связанный модуль или тип.

    Dim meth As DynamicMethod = new DynamicMethod("", Nothing, Nothing)
    Dim il As ILGenerator = meth.GetILGenerator()
    il.EmitWriteLine("Hello, World!")
    il.Emit(OpCodes.Ret)
    
    DynamicMethod meth = new DynamicMethod("", null, null);
    ILGenerator il = meth.GetILGenerator();
    il.EmitWriteLine("Hello, World!");
    il.Emit(OpCodes.Ret);
    

    Если анонимно размещенный динамический метод использует только открытые типы и методы, ему не требуется ограниченный доступ к элементам и он может не пропускать проверки видимости, выполняемые JIT-компилятором.

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

  • Кроме того, создайте анонимно размещенный динамический метод с ограниченной возможности пропуска проверок видимости, выполняемых JIT-компилятором, посредством использования конструктора DynamicMethod(String, Type, array<Type[], Boolean) и указания значения true для параметра restrictedSkipVisibility.

    Dim meth As New DynamicMethod("", _
                                  GetType(Char), _
                                  New Type() {GetType(String)}, _
                                  True)
    
    DynamicMethod meth = new DynamicMethod("",
                                           typeof(char), 
                                           new Type[] { typeof(String) }, 
                                           true);
    

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

    Анонимно размещенные динамические методы могут использовать эту ограниченную возможность пропуска проверок видимости, выполняемых JIT-компилятором, только в том случае, если приложение предоставляет разрешение ReflectionPermission с флагом ReflectionPermissionFlag.RestrictedMemberAccess. Это разрешение требуется при вызове метода.

    Bb384237.alert_note(ru-ru,VS.90).gifПримечание.

    Сведения стека вызовов для выпускающей сборки включаются при создании динамического метода. Поэтому запрос делается для разрешений выпускающей сборки, а не для сборки, вызывающей метод. Это препятствует выполнению выпущенного кода с повышенным уровнем разрешений.

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

    Bb384237.alert_note(ru-ru,VS.90).gifПримечание.

    Ограниченная возможность пропуска проверок видимости — это функциональная возможность анонимно размещенных динамических методов. Если обычные динамические методы пропускают проверку видимости, выполняемую JIT-компилятором, им следует предоставить разрешение ReflectionPermission с флагом ReflectionPermissionFlag.MemberAccess. Кроме того, обычные динамические методы, получающие доступ к конфиденциальным данным в сборках, отличных от выпускающей сборки, должны иметь разрешение ReflectionPermission с флагом RestrictedMemberAccess или разрешение SecurityPermission с флагом SecurityPermissionFlag.ControlEvidence.

Пример

Описание

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

В примере определяется класс Worker, который может маршалироваться через границы домена приложений. Этот класс содержит две перегрузки метода AccessPrivateMethod, которые выпускают и выполняют динамические методы. Первая перегрузка выпускает динамический метод, который вызывает закрытый метод PrivateMethod класса Worker. Она может вызвать динамический метод с проверкой видимости с помощью JIT-компилятора или без нее. Вторая перегрузка выпускает динамический метод, который получает доступ к свойству internal (свойство Friend в Visual Basic) класса String.

В примере используется вспомогательный метод для получения набора разрешений Internet из политики безопасности, затем создается домен приложения с помощью перегрузки метода AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) для указания необходимости использования для всего выполняемого кода в домене этого разрешения. В примере создается экземпляр класса Worker в домене приложения, затем дважды выполняется метод AccessPrivateMethod.

  • При первом запуске метода AccessPrivateMethod принудительно выполняются проверки видимости с помощью JIT-компилятора. Динамический метод завершается со сбоем после вызова, так как проверки видимости с помощью JIT-компилятора не дают ему получить доступ к закрытому методу.

  • При втором запуске метода AccessPrivateMethod проверки видимости с помощью JIT-компилятора не выполняются. Компиляция динамического метода завершается со сбоем, так как набор разрешений Internet не предоставляет достаточных разрешений для пропуска проверок видимости.

В примере используется вспомогательный метод для получения набора разрешений Internet, затем в этот набор добавляется ReflectionPermission с ReflectionPermissionFlag.RestrictedMemberAccess. Затем в примере создается второй домен и указывается, что весь код, выполняемый в домене, получает разрешения из нового набора разрешений. В примере создается экземпляр класса Worker в домене приложения, затем дважды выполняются перегрузки метода AccessPrivateMethod.

  • Выполняется первая перегрузка метода AccessPrivateMethod, и проверки видимости с помощью JIT-компилятора пропускаются. Динамический метод успешно компилируется и выполняется, так как сборка, выпустившая код является такой же, что и сборка, содержащая закрытый метод. Поэтому уровни доверия одинаковы. Если приложение, содержащее класс Worker содержит несколько сборок, этот же процесс пройдет успешно для любой из сборок, так как они все будут иметь один и тот же уровень доверия.

  • Выполняется вторая перегрузка метода AccessPrivateMethod, и проверки видимости с помощью JIT-компилятора снова пропускаются. На этом раз компиляция динамического метода завершается со сбоем, потому что происходит попытка получить доступ к свойству internalFirstChar класса String. Сборка, содержащая класс String, имеет полное доверие. Следовательно, уровень доверия выше, чем уровень доверия сборки, выпустившей код.

В этом сравнении показано, как ReflectionPermissionFlag.RestrictedMemberAccess позволяет пропускать проверки видимости в коде с неполным доверием, предназначенные для другого кода с неполным доверием, без нарушения безопасности доверенного кода.

Код

Imports System
Imports System.Reflection.Emit
Imports System.Reflection
Imports System.Security
Imports System.Security.Permissions
Imports System.Security.Policy
Imports System.Collections
Imports System.Diagnostics

' This code example works properly only if it is run from a fully 
' trusted location, such as your local computer.

' Delegates used to execute the dynamic methods.
'
Public Delegate Sub Test(ByVal w As Worker) 
Public Delegate Sub Test1() 
Public Delegate Function Test2(ByVal instance As String) As Char 

' The Worker class must inherit MarshalByRefObject so that its public 
' methods can be invoked across application domain boundaries.
'
Public Class Worker
    Inherits MarshalByRefObject

    Private Sub PrivateMethod() 
        Console.WriteLine("Worker.PrivateMethod()")
    End Sub 

    Public Sub SimpleEmitDemo()

        Dim meth As DynamicMethod = new DynamicMethod("", Nothing, Nothing)
        Dim il As ILGenerator = meth.GetILGenerator()
        il.EmitWriteLine("Hello, World!")
        il.Emit(OpCodes.Ret)

        Dim t1 As Test1 = CType(meth.CreateDelegate(GetType(Test1)), Test1)
        t1()
    End Sub

    ' This overload of AccessPrivateMethod emits a dynamic method and
    ' specifies whether to skip JIT visiblity checks. It creates a 
    ' delegate for the method and invokes the delegate. The dynamic 
    ' method calls a private method of the Worker class.
    Overloads Public Sub AccessPrivateMethod( _
                       ByVal restrictedSkipVisibility As Boolean) 

        ' Create an unnamed dynamic method that has no return type,
        ' takes one parameter of type Worker, and optionally skips JIT
        ' visiblity checks.
        Dim meth As New DynamicMethod("", _
                                      Nothing, _
                                      New Type() { GetType(Worker) }, _
                                      restrictedSkipVisibility)

        ' Get a MethodInfo for the private method.
        Dim pvtMeth As MethodInfo = GetType(Worker).GetMethod( _
            "PrivateMethod", _
            BindingFlags.NonPublic Or BindingFlags.Instance)

        ' Get an ILGenerator and emit a body for the dynamic method.
        Dim il As ILGenerator = meth.GetILGenerator()

        ' Load the first argument, which is the target instance, onto the
        ' execution stack, call the private method, and return.
        il.Emit(OpCodes.Ldarg_0)
        il.EmitCall(OpCodes.Call, pvtMeth, Nothing)
        il.Emit(OpCodes.Ret)

        ' Create a delegate that represents the dynamic method, and 
        ' invoke it. 
        Try
            Dim t As Test = CType(meth.CreateDelegate(GetType(Test)), Test)
            Try
                t(Me)
            Catch ex As Exception
                Console.WriteLine("{0} was thrown when the delegate was invoked.", _
                    ex.GetType().Name)
            End Try
        Catch ex As Exception
            Console.WriteLine("{0} was thrown when the delegate was compiled.", _
                ex.GetType().Name)
        End Try

    End Sub 


    ' This overload of AccessPrivateMethod emits a dynamic method that takes
    ' a string and returns the first character, using a private field of the 
    ' String class. The dynamic method skips JIT visiblity checks.
    Overloads Public Sub AccessPrivateMethod() 

        Dim meth As New DynamicMethod("", _
                                      GetType(Char), _
                                      New Type() {GetType(String)}, _
                                      True)

        ' Get a MethodInfo for the 'get' accessor of the private property.
        Dim pi As PropertyInfo = GetType(String).GetProperty( _
            "FirstChar", _
            BindingFlags.NonPublic Or BindingFlags.Instance) 
        Dim pvtMeth As MethodInfo = pi.GetGetMethod(True)

        ' Get an ILGenerator and emit a body for the dynamic method.
        Dim il As ILGenerator = meth.GetILGenerator()

        ' Load the first argument, which is the target string, onto the
        ' execution stack, call the 'get' accessor to put the result onto 
        ' the execution stack, and return.
        il.Emit(OpCodes.Ldarg_0)
        il.EmitCall(OpCodes.Call, pvtMeth, Nothing)
        il.Emit(OpCodes.Ret)

        ' Create a delegate that represents the dynamic method, and 
        ' invoke it. 
        Try
            Dim t As Test2 = CType(meth.CreateDelegate(GetType(Test2)), Test2)
            Dim first As Char = t("Hello, World!")
            Console.WriteLine("{0} is the first character.", first)
        Catch ex As Exception
            Console.WriteLine("{0} was thrown when the delegate was compiled.", _
                ex.GetType().Name)
        End Try

    End Sub 
End Class

Friend Class Example

    ' The entry point for the code example.
    Shared Sub Main() 

        ' Get the display name of the executing assembly, to use when
        ' creating objects to run code in application domains.
        Dim asmName As String = [Assembly].GetExecutingAssembly().FullName

        ' Create evidence for a partially trusted location and a setup object
        ' that specifies the current directory for the application directory.
        Dim zoneEvidence() As Object = { New Zone(SecurityZone.Internet) }
        Dim internetZone As New Evidence(zoneEvidence, zoneEvidence)
        Dim adSetup As New AppDomainSetup()
        adSetup.ApplicationBase = "."

        ' Retrieve the Internet grant set from system policy, and create 
        ' an application domain in which all code that executes is granted
        ' the permissions of an application run from the Internet.
        Dim internetSet As PermissionSet = GetNamedPermissionSet("Internet")
        Dim ad As AppDomain = AppDomain.CreateDomain("ChildDomain1", _
                                                     internetZone, _
                                                     adSetup, _
                                                     internetSet, _
                                                     Nothing)

        ' Create an instance of the Worker class in the partially trusted 
        ' domain. Note: If you build this code example in Visual Studio, 
        ' you must change the name of the class to include the default 
        ' namespace, which is the project name. For example, if the project
        ' is "AnonymouslyHosted", the class is "AnonymouslyHosted.Worker".
        Dim w As Worker = _
            CType(ad.CreateInstanceAndUnwrap(asmName, "Worker"), Worker)

        ' Emit a simple dynamic method that prints "Hello, World!"
        w.SimpleEmitDemo()

        ' Emit and invoke a dynamic method that calls a private method
        ' of Worker, with JIT visibility checks enforced. The call fails 
        ' when the delegate is invoked.
        w.AccessPrivateMethod(False)

        ' Emit and invoke a dynamic method that calls a private method
        ' of Worker, skipping JIT visibility checks. The call fails when
        ' the method is compiled.
        w.AccessPrivateMethod(True)


        ' Unload the application domain. Now create a grant set composed 
        ' of the permissions granted to an Internet application plus
        ' RestrictedMemberAccess, and use it to create an application
        ' domain in which partially trusted code can call private members,
        ' as long as the trust level of those members is equal to or lower
        ' than the trust level of the partially trusted code. 
        AppDomain.Unload(ad)
        internetSet = GetNamedPermissionSet("Internet")
        internetSet.SetPermission( _
            New ReflectionPermission( _
                ReflectionPermissionFlag.RestrictedMemberAccess))
        ad = AppDomain.CreateDomain("ChildDomain2", _
                                    internetZone, _
                                    adSetup, _
                                    internetSet, _
                                    Nothing)

        ' Create an instance of the Worker class in the partially trusted 
        ' domain. 
        w = CType(ad.CreateInstanceAndUnwrap(asmName, "Worker"), Worker)

        ' Again, emit and invoke a dynamic method that calls a private method
        ' of Worker, skipping JIT visibility checks. This time compilation 
        ' succeeds because of the grant for RestrictedMemberAccess.
        w.AccessPrivateMethod(True)

        ' Finally, emit and invoke a dynamic method that calls an internal 
        ' method of the String class. The call fails, because the trust level
        ' of the assembly that contains String is higher than the trust level
        ' of the assembly that emits the dynamic method.
        w.AccessPrivateMethod()

    End Sub 


    ' This method retrieves a named permission set from security policy.
    ' The return value is the intersection of all permission sets with the
    ' given name, from all policy levels, or an empty permission set if the
    ' name is not found.
    Private Shared Function GetNamedPermissionSet(ByVal name As String) As PermissionSet 

        If (String.IsNullOrEmpty(name)) Then 
            Throw New ArgumentException("name", "Cannot search for a permission set without a name.")
        End If

        Dim foundName As Boolean = False
        Dim setIntersection As New PermissionSet(PermissionState.Unrestricted)

        ' Search all policy levels.
        Dim levelEnumerator As IEnumerator = SecurityManager.PolicyHierarchy()
        While (levelEnumerator.MoveNext())

            Dim level As PolicyLevel = levelEnumerator.Current 
            Debug.Assert(level IsNot Nothing)

            ' If this policy level has a named permission set with the 
            ' specified name, intersect it with previous levels.
            Dim levelSet As PermissionSet = level.GetNamedPermissionSet(name)
            If (levelSet IsNot Nothing) Then

                foundName = True
                setIntersection = setIntersection.Intersect(levelSet)

                ' Intersect() returns null for an empty set. If this occurs
                ' at any point, the resulting permission set is empty.
                If (setIntersection Is Nothing) Then
                    Return New PermissionSet(PermissionState.None)
                End If
            End If
        End While

        If Not foundName Then
            setIntersection = New PermissionSet(PermissionState.None)
        End If
        Return setIntersection

    End Function 

End Class 

' This code example produces the following output:
'
'Hello, World!
'MethodAccessException was thrown when the delegate was invoked.
'MethodAccessException was thrown when the delegate was compiled.
'Worker.PrivateMethod()
'MethodAccessException was thrown when the delegate was compiled.
' 
using System;
using System.Reflection.Emit;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
using System.Collections;
using System.Diagnostics;

// This code example works properly only if it is run from a fully 
// trusted location, such as your local computer.

// Delegates used to execute the dynamic methods.
//
public delegate void Test(Worker w);
public delegate void Test1();
public delegate char Test2(String instance);

// The Worker class must inherit MarshalByRefObject so that its public 
// methods can be invoked across application domain boundaries.
//
public class Worker : MarshalByRefObject
{
    private void PrivateMethod()
    {
        Console.WriteLine("Worker.PrivateMethod()");
    }

    public void SimpleEmitDemo()
    {
        DynamicMethod meth = new DynamicMethod("", null, null);
        ILGenerator il = meth.GetILGenerator();
        il.EmitWriteLine("Hello, World!");
        il.Emit(OpCodes.Ret);

        Test1 t1 = (Test1) meth.CreateDelegate(typeof(Test1));
        t1();
    }

    // This overload of AccessPrivateMethod emits a dynamic method and
    // specifies whether to skip JIT visiblity checks. It creates a 
    // delegate for the method and invokes the delegate. The dynamic 
    // method calls a private method of the Worker class.
    public void AccessPrivateMethod(bool restrictedSkipVisibility) 
    {
        // Create an unnamed dynamic method that has no return type,
        // takes one parameter of type Worker, and optionally skips JIT
        // visiblity checks.
        DynamicMethod meth = new DynamicMethod(
            "", 
            null, 
            new Type[] { typeof(Worker) }, 
            restrictedSkipVisibility);

        // Get a MethodInfo for the private method.
        MethodInfo pvtMeth = typeof(Worker).GetMethod("PrivateMethod",
            BindingFlags.NonPublic | BindingFlags.Instance);

        // Get an ILGenerator and emit a body for the dynamic method.
        ILGenerator il = meth.GetILGenerator();

        // Load the first argument, which is the target instance, onto the
        // execution stack, call the private method, and return.
        il.Emit(OpCodes.Ldarg_0);
        il.EmitCall(OpCodes.Call, pvtMeth, null);
        il.Emit(OpCodes.Ret);

        // Create a delegate that represents the dynamic method, and 
        // invoke it. 
        try 
        {
            Test t = (Test) meth.CreateDelegate(typeof(Test));
            try 
            {
                t(this);
            }
            catch (Exception ex) 
            {
                Console.WriteLine("{0} was thrown when the delegate was invoked.", 
                    ex.GetType().Name);
            }
        } 
        catch (Exception ex) 
        {
            Console.WriteLine("{0} was thrown when the delegate was compiled.", 
                ex.GetType().Name);
        }
    }

    // This overload of AccessPrivateMethod emits a dynamic method that takes
    // a string and returns the first character, using a private field of the 
    // String class. The dynamic method skips JIT visiblity checks.
    public void AccessPrivateMethod() 
    {
        DynamicMethod meth = new DynamicMethod("",
                                               typeof(char), 
                                               new Type[] { typeof(String) }, 
                                               true);

        // Get a MethodInfo for the 'get' accessor of the private property.
        PropertyInfo pi = typeof(System.String).GetProperty(
            "FirstChar",
            BindingFlags.NonPublic | BindingFlags.Instance);
        MethodInfo pvtMeth = pi.GetGetMethod(true);

        // Get an ILGenerator and emit a body for the dynamic method.
        ILGenerator il = meth.GetILGenerator();

        // Load the first argument, which is the target string, onto the
        // execution stack, call the 'get' accessor to put the result onto 
        // the execution stack, and return.
        il.Emit(OpCodes.Ldarg_0);
        il.EmitCall(OpCodes.Call, pvtMeth, null);
        il.Emit(OpCodes.Ret);

        // Create a delegate that represents the dynamic method, and 
        // invoke it. 
        try 
        {
            Test2 t = (Test2) meth.CreateDelegate(typeof(Test2));
            char first = t("Hello, World!");
            Console.WriteLine("{0} is the first character.", first);
        } 
        catch (Exception ex) 
        {
            Console.WriteLine("{0} was thrown when the delegate was compiled.", 
                ex.GetType().Name);
        }
    }


    // The entry point for the code example.
    static void Main()
    {
        // Get the display name of the executing assembly, to use when
        // creating objects to run code in application domains.
        String asmName = Assembly.GetExecutingAssembly().FullName;

        // Create evidence for a partially trusted location and a setup object
        // that specifies the current directory for the application directory.
        Object[] zoneEvidence = { new Zone(SecurityZone.Internet) };
        Evidence internetZone = new Evidence(zoneEvidence, zoneEvidence);
        AppDomainSetup adSetup = new AppDomainSetup();
        adSetup.ApplicationBase = ".";

        // Retrieve the Internet grant set from system policy, and create 
        // an application domain in which all code that executes is granted
        // the permissions of an application run from the Internet.
        PermissionSet internetSet = GetNamedPermissionSet("Internet");
        AppDomain ad = AppDomain.CreateDomain("ChildDomain1", 
                                              internetZone, 
                                              adSetup, 
                                              internetSet, 
                                              null);

        // Create an instance of the Worker class in the partially trusted 
        // domain. Note: If you build this code example in Visual Studio, 
        // you must change the name of the class to include the default 
        // namespace, which is the project name. For example, if the project
        // is "AnonymouslyHosted", the class is "AnonymouslyHosted.Worker".
        Worker w = (Worker) ad.CreateInstanceAndUnwrap(asmName, "Worker");

        // Emit a simple dynamic method that prints "Hello, World!"
        w.SimpleEmitDemo();

        // Emit and invoke a dynamic method that calls a private method
        // of Worker, with JIT visibility checks enforced. The call fails 
        // when the delegate is invoked.
        w.AccessPrivateMethod(false);

        // Emit and invoke a dynamic method that calls a private method
        // of Worker, skipping JIT visibility checks. The call fails when
        // the method is compiled.
        w.AccessPrivateMethod(true);


        // Unload the application domain. Now create a grant set composed 
        // of the permissions granted to an Internet application plus
        // RestrictedMemberAccess, and use it to create an application
        // domain in which partially trusted code can call private members,
        // as long as the trust level of those members is equal to or lower
        // than the trust level of the partially trusted code. 
        AppDomain.Unload(ad);
        internetSet = GetNamedPermissionSet("Internet");
        internetSet.SetPermission(
            new ReflectionPermission(
                ReflectionPermissionFlag.RestrictedMemberAccess));
        ad = AppDomain.CreateDomain("ChildDomain2", 
                                    internetZone, 
                                    adSetup, 
                                    internetSet, 
                                    null);

        // Create an instance of the Worker class in the partially trusted 
        // domain. 
        w = (Worker) ad.CreateInstanceAndUnwrap(asmName, "Worker");

        // Again, emit and invoke a dynamic method that calls a private method
        // of Worker, skipping JIT visibility checks. This time compilation 
        // succeeds because of the grant for RestrictedMemberAccess.
        w.AccessPrivateMethod(true);

        // Finally, emit and invoke a dynamic method that calls an internal 
        // method of the String class. The call fails, because the trust level
        // of the assembly that contains String is higher than the trust level
        // of the assembly that emits the dynamic method.
        w.AccessPrivateMethod();
    }


    // This method retrieves a named permission set from security policy.
    // The return value is the intersection of all permission sets with the
    // given name, from all policy levels, or an empty permission set if the
    // name is not found.
    private static PermissionSet GetNamedPermissionSet(string name)
    {
        if (String.IsNullOrEmpty(name))
            throw new ArgumentException("name", "Cannot search for a permission set without a name.");

        bool foundName = false;
        PermissionSet setIntersection = new PermissionSet(PermissionState.Unrestricted);

        // Search all policy levels.
        IEnumerator levelEnumerator = SecurityManager.PolicyHierarchy();
        while (levelEnumerator.MoveNext())
        {
            PolicyLevel level = levelEnumerator.Current as PolicyLevel;
            Debug.Assert(level != null);

            // If this policy level has a named permission set with the 
            // specified name, intersect it with previous levels.
            PermissionSet levelSet = level.GetNamedPermissionSet(name);
            if (levelSet != null)
            {
                foundName = true;
                setIntersection = setIntersection.Intersect(levelSet);

                // Intersect() returns null for an empty set. If this occurs
                // at any point, the resulting permission set is empty.
                if (setIntersection == null)
                    return new PermissionSet(PermissionState.None);
            }
        }

        if (!foundName)
            setIntersection = new PermissionSet(PermissionState.None);

        return setIntersection;
    }
}

/* This code example produces the following output:

Hello, World!
MethodAccessException was thrown when the delegate was invoked.
MethodAccessException was thrown when the delegate was compiled.
Worker.PrivateMethod()
MethodAccessException was thrown when the delegate was compiled.
 */

Компиляция кода

  • При создании этого примера кода в Visual Studio необходимо изменить имя класса для включения пространства имен при передаче его в метод CreateInstanceAndUnwrap. По умолчанию пространство имен носит имя проекта. Например, если проект называется "PartialTrust", именем класса должно являться "PartialTrust.Worker".

См. также

Задачи

Практическое руководство. Выполнение не вполне безопасного кода в изолированной среде

Основные понятия

Вопросы безопасности в порождаемом отражении