Udostępnij za pośrednictwem


Optymalizacje zabezpieczeń

Sprawdzanie zabezpieczeń może powodować problemy z wydajnością niektórych aplikacji. Istnieją dwie techniki optymalizacji, które można użyć w celu zwiększenia wydajności. Jedna technika uwzględnia wymagania bezpieczeństwa, druga pomija zapotrzebowanie na uprawnienia do wywoływania niezarządzanego kodu. Chociaż te techniki mogą poprawiać działanie aplikacji, mogą także narazić aplikację na naruszenia luk z zabezpieczeniach. Przed użyciem tych technik optymalizacji należy podjąć następujące środki ostrożności:

  • Postępuj zgodnie z Wytyczne dotyczące bezpiecznego programowania dla kodu zarządzanego.

  • Zrozumienie wpływu zabezpieczeń optymalizacji i korzystanie z innych metod w celu ochrony aplikacji we właściwy sposób.

  • Zaimplementuj minimalną optymalizację zabezpieczeń wymaganą do poprawy działania aplikacji.

Po optymalizacji kodu należy przetestować zoptymalizowany kodu w celu ustalenia, czy poprawiło się faktycznie jego działanie. Jeśli nie, należy usunąć optymalizacje zabezpieczeń, aby zapobiec nieumyślnemu tworzeniu luk w zabezpieczeniach.

Ostrzeżenie

Optymalizacja zabezpieczeń wymaga zmiany zabezpieczeń dostępu do standardowego kodu.W celu uniknięcia wprowadzenia do kodu luk w zabezpieczeniach, upewnij się, że rozumiesz wpływ technik optymalizacji na zabezpieczenia przed ich użyciem.

Łączenie wymogów bezpieczeństwa

Aby zoptymalizować kod z z uwzględnieniem wymogów bezpieczeństwa, w niektórych sytuacjach można użyć techniki łączenia wymagań.

Na przykład, jeśli:

  • Twój kod wykonuje pewną liczbę operacji w ramach jednej metody oraz

  • Podczas wykonywania każdej z tych operacji, kod wywołuje bibliotekę klas zarządzanych, co wymaga, aby kod miał to samo uprawnienie na każde wywołanie biblioteki,

następnie:

  • można zmodyfikować kod do wykonywania żądania i potwierdzania tego uprawnienia, aby zmniejszyć obciążenie poniesione przez wymogi bezpieczeństwa.

Jeśli głębokość stosu wywołań powyżej tej metoda jest duża, użycie tej techniki może spowodować znaczną poprawę wydajności działania.

Aby zilustrować, jak to działa, załóżmy, że metoda M wykonuje 100 operacji. Każda operacja powoduje wywołanie do biblioteki, która wykonuje żądanie zabezpieczeń wymagające, aby Twój kod i jego wszystkie obiekty wywołujące miały uprawnienie X. Ze względu na bezpieczeństwo każda operacja powoduje, że środowisko wykonawcze przeszukuje cały stos wywołań, badając uprawnienia każdego obiektu wywołującego, aby ustalić, czy rzeczywiście przyznano uprawnienie X do każdego obiektu wywołującego. Jeśli stos wywołań powyżej metody M ma n poziomów głębokości, są wymagane porównania 100n.

Aby zoptymalizować, można wykonać następujące czynności metody M:

  • Żądanie X, którego wynikiem jest to, że środowisko wykonawcze dokonuje przeszukania stosu (do głębokości n) w celu zapewnienia, że wszystkie obiekty wywołujące w istocie mają uprawnienie X.

  • Następnie potwierdź uprawnienie X powodujące kolejne zatrzymanie przeszukiwania stosu w metodzie M oraz kontynuuj, obniżając w ten sposób liczbę porównań uprawnień o 99n.

W poniższym przykładzie kodu metoda GetFileCreationTime pobiera reprezentację ciągu z katalogu jako parametr i wyświetla nazwę oraz datę utworzenia każdego pliku w tym katalogu. Statyczna metoda File.GetCreationTime odczytuje informacje z plików, ale wymaga przeszukiwania popytu i stosu w każdym odczytywanym pliku. Metoda tworzy nowe wystąpienie obiektuFileIOPermission, wykonuje żądanie, aby sprawdzić uprawnienia wszystkich wywołujących na stosie i potwierdza uprawnienia, jeśli żądanie zakończyło się pomyślnie. Jeśli żądanie powiedzie się, jest wykonywane tylko jedno przeszukiwanie stosu i metoda odczytuje godzinę utworzenia z każdego w przekazanym katalogu.

using System;
using System.IO;
using System.Security;
using System.Security.Permissions;

namespace OptimizedSecurity
{
   public class FileUtil
   {
      public FileUtil()
      {
      }

      public void GetFileCreationTime(string Directory)
      {
         //Initialize DirectoryInfo object to the passed directory. 
         DirectoryInfo DirFiles = new DirectoryInfo(Directory);

         //Create a DateTime object to be initialized below.
         DateTime TheTime;

         //Get a list of files for the current directory.
         FileInfo[] Files = DirFiles.GetFiles();
         
         //Create a new instance of FileIOPermission with read 
         //permission to the current directory.
         FileIOPermission FilePermission = new FileIOPermission(FileIOPermissionAccess.Read, Directory);

         try
         {
            //Check the stack by making a demand.
            FilePermission.Demand();

            //If the demand succeeded, assert permission and 
            //perform the operation.
            FilePermission.Assert();

            for(int x = 0 ; x<= Files.Length -1 ; x++)
            {
               TheTime = File.GetCreationTime(Files[x].FullName);
               Console.WriteLine("File: {0} Created: {1:G}", Files[x].Name,TheTime );
            }
            // Revert the Assert when the operation is complete.
            CodeAccessPermission.RevertAssert();
         }
         //Catch a security exception and display an error.
         catch(SecurityException)
         {
            Console.WriteLine("You do not have permission to read this directory.");
         }                            
      }
   }
}
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security
Imports System.Security.Permissions
Namespace OptimizedSecurity
   Public Class FileUtil      
      Public Sub New()
      End Sub
      Public Sub GetFileCreationTime(directory As String)
         'Initialize DirectoryInfo object to the passed directory. 
         Dim dirFiles As New DirectoryInfo(directory)
         'Create a DateTime object to be initialized below.
         Dim theTime As DateTime
         'Get a list of files for the current directory.
         Dim files As FileInfo() = dirFiles.GetFiles()
         'Create a new instance of FileIOPermission with read 
         'permission to the current directory.
         Dim filePermission As New FileIOPermission(FileIOPermissionAccess.Read, Directory)
         Try
            'Check the stack by making a demand.
            filePermission.Demand()
            'If the demand succeeded, assert permission and 
            'perform the operation.
            filePermission.Assert()
            Dim x As Integer
            For x = 0 To Files.Length - 1
               theTime = file.GetCreationTime(files(x).FullName)
               Console.WriteLine("File: {0} Created: {1:G}", files(x).Name, theTime)
            Next x
            ' Revert the Assert when the operation is complete.
            CodeAccessPermission.RevertAssert()
         'Catch a security exception and display an error.
         Catch
            Console.WriteLine("You do not have permission to read this directory.")
         End Try
      End Sub
   End Class
End Namespace

Jeśli powiedzie się żądanie z poprzedniego przykładu, każdy plik oraz data i godzina jego utworzenia są wyświetlane dla przekazanego katalogu. Jeśli żądanie nie powiedzie się, wyjątek zabezpieczeń zostaje przechwycony i na konsoli jest wyświetlany następujący komunikat:

You do not have permission to read this directory.

Pomijanie żądań uprawnień kodu niezarządzanego.

Jest dostępna specjalna optymalizacja dla kodu, który ma uprawnienia do wywoływania niezarządzanego kodu. Optymalizacja ta pozwala kodu zarządzanemu wywoływać kod niezarządzany bez powodowania obciążeń towarzyszących przeszukiwaniu stosu. Potwierdzenie uprawnienia do kodu niezarządzanego może skrócić przeszukiwanie stosu, ale optymalizacja opisana w tym temacie może je całkowicie wyeliminować. (Zobacz SecurityPermission, aby uzyskać więcej informacji dotyczących uprawnień potrzebnych do wywoływania kodu niezarządzanego.)

Normalnie wywołanie do kodu niezarządzanego podnosi żądanie uprawnienia wobec kodu niezarządzanego, co skutkuje przeszukiwaniem stosu, które określa, czy wszystkie obiekty wywołujące mają uprawnienia pozwalające na wywoływanie niezarządzanego kodu. Zastosowanie niestandardowego atrybutu SuppressUnmanagedCodeSecurityAttribute do metody, która wywołuje niezarządzany kod, powoduje pominięcie żądania. Atrybut ten zastępuje pełne przeszukanie stosu w czasie uruchomienia sprawdzeniem wyłącznie uprawnienień bezpośredniego wywołującego w czasie łącza. W rezultacie użycie tego atrybutu tworzy otwarte drzwi wejściowe do kodu niezarządzanego. Jedynie kod z uprawnieniami do kodu niezarządzanego może korzystać z tej właściwości. W innym razie — dany efekt nie działa.

Ostrzeżenie

Atrybutu SuppressUnmanagedCodeSecurityAttribute należy używać z wyjątkową rozwagą.Nieprawidłowe użycie tego atrybutu można powodować osłabienie zabezpieczeń.Atrybut SuppressUnmanagedCodeSecurityAttribute nie powinien być nigdy wykorzystywany w celu umożliwienia sytuacji, w której mniej zaufany kod (kod, który nie ma uprawnień niezarządzanego kodu) wywoływa kod niezarządzany.

Ten atrybut najlepiej stosuje się tylko do punktów wejścia zadeklarowanych prywatnie do niezarządzanego kodu, tak aby kod w innych zestawach nie miał dostępu i nie pomijał zabezpieczeń. Zazwyczaj wysoce zaufany kod zarządzany, który używa tego atrybutu, najpierw żąda niektórych uprawnień obiektów wywołujących przed wywoływaniem niezarządzanego kodu w imieniu obiektu wywołującego.

W poniższym przykładzie przedstawiono atrybut SuppressUnmanagedCodeSecurityAttribute zastosowany do prywatnego punktu wejścia.

<SuppressUnmanagedCodeSecurityAttribute()> Private Declare Sub 
EntryPoint Lib "some.dll"(args As String)
[SuppressUnmanagedCodeSecurityAttribute()]
[DllImport("some.dll")]
private static extern void EntryPoint(string args);

W rzadkim przypadku niezarządzanego kodu, który jest całkowicie bezpieczny dla wszystkich możliwych okoliczności, metoda z atrybutem SuppressUnmanagedCodeSecurityAttribute może być udostępniana innemu kodowi zarządzanemu bezpośrednio przez uczynienie jej publiczną zamiast prywatnej. Jeśli zdecydujesz się ujawnić metodę, która ma atrybut SuppressUnmanagedCodeSecurityAttribute, funkcje kodu niezarządzanego muszą być nie tylko bezpieczne, ale również nieprzepuszczalne dla ataków przez złośliwe obiekty wywołujące. Na przykład kod musi działać poprawnie nawet kiedy niepożądane argumenty są fabrykowane, aby specjalnie spowodować niepoprawne działanie kodu.

Używanie deklaracyjnych zastąpień i imperatywnych żądań

Potwierdzenia i inne zastąpienia są najszybsze, gdy są dokonywane deklaracyjnie, natomiast żądania są najszybsze przy wykonywaniu imperatywnym. Chociaż wzrost wydajności może nie być znaczący, używanie deklaracyjnych zastąpień i imperatywnych żądań może pomóc poprawić działanie kodu.

Zobacz też

Informacje

File.GetCreationTime

SecurityPermission

SuppressUnmanagedCodeSecurityAttribute

Koncepcje

Pisanie zabezpieczonych bibliotek klas

Inne zasoby

Zabezpieczenia dostępu kodu