Optimisations de la sécurité
Mise à jour : novembre 2007
Les vérifications de sécurité peuvent entraîner des problèmes de performance pour certaines applications. Deux techniques d'optimisation s'offrent à vous pour vous permettre d'améliorer les performances. Une technique combine les demandes et l'autre supprime les demandes d'autorisation d'appeler dans du code non managé. Si ces techniques peuvent améliorer les performances de votre application, elles peuvent également rendre votre application vulnérable aux exploitations de la sécurité. Avant d'utiliser ces techniques d'optimisation, vous devez prendre les précautions suivantes :
Consultez la section Indications de codage sécurisé pour le code managé.
Évaluez les implications des optimisations en matière de sécurité et utilisez d'autres méthodes pour protéger vos applications de manière appropriée.
Implémentez les optimisations minimales de sécurité nécessaires à l'amélioration des performances des applications.
Après optimisation de votre code, vous devez tester le code optimisé pour déterminer si ses performances ont fait l'objet d'une réelle amélioration. Dans le cas contraire, vous devez supprimer les optimisations de la sécurité pour empêcher des failles accidentelles dans la sécurité.
Attention : |
---|
Les optimisations de la sécurité vous obligent à changer la sécurité d'accès du code standard. Afin d'éviter d'introduire des failles de sécurité dans votre code, veillez à bien évaluer les implications en matière de sécurité des techniques d'optimisation avant de les utiliser. |
Combinaison des demandes de sécurité
Pour optimiser du code qui procède aux demandes de sécurité, vous pouvez dans certains cas utiliser une technique de combinaison des demandes.
Par exemple, si :
votre code effectue un certain nombre d'opérations dans une méthode unique et
lorsqu'il exécute chacune de ces opérations, votre code appelle une bibliothèque de classes managée qui exige que votre code ait la même autorisation pour chaque appel à la bibliothèque,
alors :
- vous pouvez modifier votre code pour effectuer un Demand et un Assert de cette autorisation afin de réduire la charge provoquée par les demandes de sécurité.
Si la profondeur de la pile des appels au-dessus de la méthode est importante, l'utilisation de cette technique peut entraîner un gain de performance considérable.
Pour en illustrer le fonctionnement, supposons que la méthode M effectue 100 opérations. Chaque opération appelle dans une bibliothèque qui fait une demande de sécurité exigeant que votre code et tous ses appelants aient l'autorisation X. En raison des demandes de sécurité, chaque opération conduit le runtime à parcourir l'intégralité de la pile des appels pour examiner les autorisations de chaque appelant, afin de déterminer si l'autorisation X lui a réellement été octroyée. Si la pile des appels au-dessus de la méthode M est profonde de n niveaux, 100n comparaisons sont requises.
Pour optimiser cela, vous pouvez procéder comme suit dans la méthode M :
Demandez X, ce qui conduit le runtime à effectuer un parcours de pile (de profondeur n) pour s'assurer que tous les appelants ont effectivement l'autorisation X.
Déclarez ensuite l'autorisation X, ce qui provoque l'arrêt des parcours de pile ultérieurs au niveau de la méthode M et leur réussite, réduisant ainsi le nombre de comparaisons d'autorisations de 99n.
Dans l'exemple de code suivant, la méthode GetFileCreationTime prend comme paramètre une représentation de type chaîne d'un répertoire et affiche le nom et la date de création de chaque fichier dans ce répertoire. La méthode statique File.GetCreationTime lit les informations provenant des fichiers, mais exige une demande et un parcours de pile pour chaque fichier lu. La méthode crée une nouvelle instance de l'objet FileIOPermission, effectue une demande de vérification des autorisations de tous les appelants de la pile puis déclare l'autorisation si la demande réussit. Si la demande réussit, seul un parcours de pile est effectué et la méthode lit l'heure de création de chaque fichier dans le répertoire passé.
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
Si la demande de l'exemple précédent réussit, chaque fichier ainsi que sa date et son heure de création sont alors affichés pour le répertoire passé. Si la demande échoue, l'exception de sécurité est alors interceptée et le message suivant est affiché sur la console :
You do not have permission to read this directory.
Suppression des demandes d'autorisation de code non managé
Une optimisation spéciale est disponible pour du code qui a l'autorisation d'appeler du code non managé. Cette optimisation permet à votre code managé d'appeler dans du code non managé sans provoquer la charge d'un parcours de pile. L'assertion de l'autorisation de code non managé peut réduire le parcours de pile mais l'optimisation décrite dans cette rubrique peut l'éliminer totalement. (Consultez SecurityPermission pour plus d'informations concernant l'autorisation d'appel dans du code non managé.)
Un appel dans du code non managé déclenche normalement une demande d'autorisation de code non managé, ce qui entraîne un parcours de pile qui détermine si tous les appelants ont l'autorisation d'appeler dans du code non managé. L'application de l'attribut personnalisé SuppressUnmanagedCodeSecurityAttribute à la méthode qui appelle dans du code non managé supprime la demande. Cet attribut remplace l'intégralité du parcours de pile au moment de l'exécution par une vérification qui contrôle uniquement les autorisations de l'appelant immédiat au moment de la liaison. En effet, l'utilisation de cet attribut crée une porte ouverte dans du code non managé. Seul du code ayant une autorisation de code non managé peut utiliser cet attribut, sinon il n'a aucun effet.
Attention : |
---|
Utilisez l'attribut SuppressUnmanagedCodeSecurityAttribute avec infiniment de précaution. L'utilisation incorrecte de cet attribut peut mettre la sécurité en danger. L'attribut SuppressUnmanagedCodeSecurityAttribute ne doit jamais être utilisé pour autoriser du code ayant un niveau de confiance plus faible (code n'ayant pas l'autorisation de code non managé) à appeler dans du code non managé. |
Cet attribut s'applique mieux et uniquement à des points d'entrée déclarés de manière privée dans du code non managé de sorte que du code dans d'autres assemblys ne puisse pas accéder et tirer profit de la suppression de la sécurité. Généralement, du code managé dont le niveau de confiance est élevé qui utilise cet attribut demande d'abord l'autorisation des appelants avant d'appeler le code non managé au nom de l'appelant.
L'exemple suivant illustre l'attribut SuppressUnmanagedCodeSecurityAttribute appliqué à un point d'entrée privé.
<SuppressUnmanagedCodeSecurityAttribute()> Private Declare Sub
EntryPoint Lib "some.dll"(args As String)
[SuppressUnmanagedCodeSecurityAttribute()]
[DllImport("some.dll")]
private static extern void EntryPoint(string args);
Dans les rares cas de code non managé totalement sûr pour toutes les circonstances possibles, une méthode avec l'attribut SuppressUnmanagedCodeSecurityAttribute peut être directement exposée à d'autre code managé en le rendant public au lieu de privé. Si vous choisissez d'exposer une méthode ayant l'attribut SuppressUnmanagedCodeSecurityAttribute, la fonctionnalité du code non managé doit non seulement être sûre mais également imperméable aux attaques d'appelants malveillants. Par exemple, le code doit s'exécuter de manière appropriée même lorsque des arguments non prévus sont fabriqués pour provoquer le dysfonctionnement du code de manière spécifique.
Utilisation de substitutions déclaratives et de demandes impératives
Les assertions et autres substitutions sont plus rapides lorsqu'elles sont effectuées de manière déclarative tandis que les demandes sont plus rapides lorsqu'elles sont faites de façon impérative. Bien que les gains de performance risquent de ne pas être considérables, l'utilisation de substitutions déclaratives et de demandes impératives peut vous aider à améliorer la performance du code.
Voir aussi
Concepts
Écriture des bibliothèques de classes sécurisées
Référence
SuppressUnmanagedCodeSecurityAttribute