Ottimizzazioni della sicurezza
Aggiornamento: novembre 2007
I controlli di sicurezza possono provocare problemi di prestazioni per alcune applicazioni. Sono disponibili due tecniche di ottimizzazione per migliorare le prestazioni. Una tecnica è basata su una combinazione delle richieste di sicurezza mentre l'altra comporta la disattivazione delle richieste di autorizzazione per chiamare codice non gestito. Anche se queste tecniche consentono di migliorare le prestazioni dell'applicazione in uso, possono anche rendere l'applicazione vulnerabile alle violazioni della sicurezza. Prima di utilizzare le tecniche di ottimizzazione, adottare le seguenti precauzioni:
Seguire le Indicazioni per la generazione di codice protetto per il codice gestito.
Approfondire le implicazioni relative alla sicurezza per le ottimizzazioni e utilizzare altri metodi per proteggere l'applicazione in modo adeguato.
Implementare solo le ottimizzazioni della sicurezza richieste per migliorare le prestazioni dell'applicazione.
Una volta ottimizzato il codice, sottoporlo a verifica per determinare se le prestazioni risultano realmente migliorate. In caso contrario, rimuovere le ottimizzazioni per impedire indebolimenti accidentali della sicurezza.
Attenzione: |
---|
L'ottimizzazione della sicurezza richiede la modifica della sicurezza standard di accesso da codice. Per evitare di introdurre elementi di vulnerabilità all'interno del codice, approfondire le implicazioni relative alla sicurezza delle tecniche di ottimizzazione prima di impiegarle. |
Combinazione di richieste di sicurezza
Per ottimizzare il codice mediante il quale si generano richieste di sicurezza, è possibile, in alcune situazioni, utilizzare una tecnica che consente di combinare le richieste.
Ad esempio, se:
il codice esegue numerose operazioni con un singolo metodo e
durante l'esecuzione di ognuna di queste operazioni viene chiamata una libreria di classi gestita ed è necessario che il codice disponga della stessa autorizzazione per ogni chiamata alla libreria,
sarà possibile:
- modificare il codice in modo che venga effettuata una chiamata di Demand e una chiamata di Assert per tale autorizzazione al fine di ridurre il sovraccarico provocato dalle richieste di sicurezza.
Se la profondità dello stack di chiamate al di sopra del metodo è elevata, l'utilizzo di questa tecnica può produrre un aumento significativo delle prestazioni.
Per comprenderne il funzionamento, si supponga che il metodo M esegua 100 operazioni. Ogni operazione chiama una libreria che genera una richiesta di sicurezza in base alla quale è necessario che il codice e tutti i relativi chiamanti dispongano dell'autorizzazione X. In seguito alle richieste di sicurezza, per ogni operazione è necessario che venga esaminato l'intero stack di chiamate alla ricerca delle autorizzazioni di ciascun chiamante, per verificare se ad ognuno di essi sia stata effettivamente concessa l'autorizzazione X. Se lo stack di chiamate al di sopra del metodo M è profondo n livelli, sarà necessario eseguire 100n confronti.
Per ottimizzare l'intero processo, includere le seguenti specificazioni nel metodo M:
Generare una richiesta di X, in base alla quale verrà eseguita un'analisi dello stack della profondità di n livelli per garantire che tutti i chiamanti dispongano dell'autorizzazione X.
Effettuare quindi un'asserzione dell'autorizzazione X, in base alla quale le successive analisi dello stack si interromperanno al metodo M e avranno esito positivo, riducendo così il numero dei confronti di autorizzazioni di 99n unità.
Nel seguente esempio di codice, il metodo GetFileCreationTime assume come parametro una rappresentazione in forma di stringa di una directory e visualizza il nome e la data di creazione di ogni file in essa contenuto. Il metodo statico File.GetCreationTime esegue la lettura delle informazioni contenute nei file, ma necessita di una richiesta e di un percorso chiamate nello stack per ogni file letto. Il metodo crea una nuova istanza dell'oggetto FileIOPermission, genera una richiesta per verificare le autorizzazioni di tutti i chiamanti compresi nello stack, quindi effettua un'asserzione dell'autorizzazione se la richiesta ha esito positivo. In tale caso, viene eseguita una sola analisi dello stack e il metodo legge la data di creazione di ciascun file presente nella directory passata.
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
Se la richiesta specificata nel precedente esempio ha esito positivo, viene visualizzato ogni file della directory passata, inclusa la data e l'ora di creazione. Se la richiesta ha esito negativo, viene intercettata la relativa eccezione di sicurezza e viene visualizzato nella console il seguente messaggio:
You do not have permission to read this directory.
Disattivazione delle richieste di autorizzazione per il codice non gestito
Per il codice autorizzato a chiamare codice non gestito è disponibile una tecnica di ottimizzazione particolare, che consente al codice gestito di chiamare codice non gestito senza provocare un overhead dell'analisi dello stack. L'analisi dello stack può essere ridotta anche mediante l'asserzione di autorizzazioni per il codice non gestito, ma con l'ottimizzazione descritta in questo argomento può essere eliminata del tutto. Per ulteriori informazioni sull'autorizzazione a chiamare codice non gestito, vedere SecurityPermission.
Chiamando codice non gestito si attiva in genere una richiesta di autorizzazione per il codice non gestito, a seguito della quale viene eseguita un'analisi dello stack per verificare che tutti i chiamanti dispongano della relativa autorizzazione. Applicando l'attributo personalizzato SuppressUnmanagedCodeSecurityAttribute al metodo che chiama il codice non gestito, la richiesta viene disattivata. Specificando questo attributo si ottiene la sostituzione in fase di esecuzione dell'intera analisi dello stack con un controllo che si limita a verificare le autorizzazioni del chiamante immediato in fase di collegamento. L'utilizzo di questo attributo produce di fatto un punto di libero accesso al codice non gestito. È possibile utilizzare questo attributo solo nel codice che dispone dell'autorizzazione per il codice non gestito, altrimenti l'attributo non avrà alcun effetto.
Attenzione: |
---|
Utilizzare l'attributo SuppressUnmanagedCodeSecurityAttribute con estrema cautela. L'utilizzo inappropriato può indebolire il sistema di sicurezza. È necessario che l'attributo SuppressUnmanagedCodeSecurityAttribute non venga mai utilizzato per consentire a codice scarsamente attendibile, vale a dire codice che non dispone della specifica autorizzazione, di chiamare codice non gestito. |
È consigliabile applicare l'attributo solo su punti di ingresso al codice non gestito dichiarati in modo privato: in questo modo il codice contenuto negli altri assembly non sarà in grado di accedere né di approfittare della disattivazione della sicurezza. Il codice gestito a elevata attendibilità che utilizza questo attributo, prima di richiamare il codice non gestito per conto del chiamante, richiede in genere che tale codice disponga di alcune autorizzazioni.
Nel seguente esempio viene illustrata l'applicazione dell'attributo SuppressUnmanagedCodeSecurityAttribute a un punto di ingresso privato.
<SuppressUnmanagedCodeSecurityAttribute()> Private Declare Sub
EntryPoint Lib "some.dll"(args As String)
[SuppressUnmanagedCodeSecurityAttribute()]
[DllImport("some.dll")]
private static extern void EntryPoint(string args);
Nei rari casi in cui il codice non gestito può essere considerato sicuro per ogni tipo di circostanza, è possibile esporre un metodo dotato dell'attributo SuppressUnmanagedCodeSecurityAttribute direttamente ad altro codice gestito dichiarandolo in modo pubblico anziché privato. Se si sceglie di esporre un metodo dotato dell'attributo SuppressUnmanagedCodeSecurityAttribute, non solo è necessario che la funzionalità del codice non gestito sia sicura, ma anche che sia resistente ai tentativi di attacchi da parte di eventuali chiamanti. È ad esempio necessario che il codice operi in modo appropriato, anche qualora argomenti imprevisti siano stati inseriti intenzionalmente al fine di provocarne malfunzionamenti.
Utilizzo di override dichiarativi e di richieste imperative
Le asserzioni e gli altri override vengono elaborati più velocemente quando effettuati in modo dichiarativo. Le richieste, invece, sono generate più velocemente in modo imperativo. L'utilizzo di override dichiarativi e di richieste imperative può aiutare a migliorare le prestazioni del codice con risultati sufficientemente apprezzabili.
Vedere anche
Concetti
Scrittura di librerie di classi protette
Riferimenti
SuppressUnmanagedCodeSecurityAttribute