Protezione dall'accesso di codice

In questa pagina
Argomenti del moduloArgomenti del modulo
ObiettiviObiettivi
Ambito di applicazioneAmbito di applicazione
Utilizzo del moduloUtilizzo del modulo
Spiegazione della protezione dall'accesso di codiceSpiegazione della protezione dall'accesso di codice
APTCAAPTCA
Codice con privilegiCodice con privilegi
Richiesta di autorizzazioniRichiesta di autorizzazioni
Autorizzazione del codiceAutorizzazione del codice
Richieste in fase di linkingRichieste in fase di linking
Assert e RevertAssertAssert e RevertAssert
Limitazione del codiceLimitazione del codice
I/O di fileI/O di file
Registro eventiRegistro eventi
Registro di sistemaRegistro di sistema
Accesso ai datiAccesso ai dati
Servizi directoryServizi directory
Variabili di ambienteVariabili di ambiente
Servizi WebServizi Web
Socket e DNSSocket e DNS
Codice non gestitoCodice non gestito
DelegatiDelegati
SerializzazioneSerializzazione
RiepilogoRiepilogo
Risorse aggiuntiveRisorse aggiuntive

Argomenti del modulo

La protezione dall'accesso di codice è un modello di limitazione delle risorse progettato per restringere i tipi di risorse di sistema accessibili dal codice e i tipi di operazioni con privilegi che il codice può eseguire. Queste limitazioni sono indipendenti dall'utente che chiama il codice o dall'account utente con cui il codice è in esecuzione. All'inizio del modulo è riportata una descrizione dettagliata del funzionamento della protezione dall'accesso di codice, seguita da una completa serie di esempi riguardanti tutte le risorse che possono effettuare l'asserzione, richiedere o consentire autorizzazioni di protezione dall'accesso di codice.

Nel modulo viene spiegato dettagliatamente il funzionamento interno della protezione dall'accesso di codice, inclusi valutazione e livello dei criteri, APTCA (AllowPartiallyTrustedCallersAttribute), codice con privilegi, richiesta di autorizzazione, autorizzazione del codice, richieste in fase di linking e asserzioni.

Nel modulo vengono descritti i tre principali vantaggi derivanti dalla protezione dall'accesso di codice e vengono illustrate le modalità di utilizzo di questo tipo di protezione per:

Limitare le azioni del codice
Supponendo ad esempio di sviluppare un assembly che esegue l'I/O di file, è possibile utilizzare la protezione dall'accesso di codice per limitare l'accesso a determinati file o cartelle. Si riduce in tal modo la possibilità che un pirata informatico possa forzare il codice per accedere a file arbitrari.

Limitare il codice autorizzato a chiamare il codice sviluppato
Potrebbe ad esempio essere opportuno fare in modo che l'assembly venga chiamato esclusivamente da altro codice sviluppato all'interno della propria organizzazione. Un modo per ottenere questo risultato consiste nell'utilizzare il componente chiave pubblica del nome sicuro di un assembly per applicare questo genere di limitazione. Si impedisce così che il proprio codice venga chiamato da codice nocivo.

Identificare il codice
Per amministrare correttamente i criteri di protezione dall'accesso di codice e limitare le azioni del codice, è necessario che quest'ultimo sia identificabile. Per identificare l'assembly la protezione dall'accesso di codice utilizza prove quali il nome sicuro o URL di un assembly, oppure il relativo hash calcolato.

Inizio paginaInizio pagina

Obiettivi

Il modulo consente di:

Sapere come funziona la protezione dall'accesso di codice e in che cosa consiste il codice con privilegi.

Utilizzare la protezione dall'accesso di codice per limitare il codice avente la necessità di eseguire l'I/O di file, accedere a dati o al registro eventi, Registro di sistema, servizi directory, variabili di ambiente, servizi Web, socket e DNS.

Applicare APTCA in maniera protetta e con efficacia.

Richiedere autorizzazioni e autorizzare il codice.

Eseguire codice con privilegi nel sandbox.

Sapere quando utilizzare e quando non utilizzare richieste in fase di linking.

Sapere quando effettuare e quando non effettuare asserzioni per le richieste di autorizzazione.

Utilizzare con efficacia le richieste di autorizzazione.

Utilizzare la protezione dall'accesso di codice per migliorare la protezione quando viene chiamato codice non gestito, utilizzando la serializzazione e i delegati.

Inizio paginaInizio pagina

Ambito di applicazione

Le informazioni contenute in questo modulo sono valide per i seguenti prodotti e tecnologie:

Microsoft Windows 2000 Server e Microsoft Windows 2003 Server

Microsoft .NET Framework 1.1

ASP.NET 1.1

Inizio paginaInizio pagina

Utilizzo del modulo

Gli argomenti di questo modulo riprendono quelli del modulo 7 "Creazione di assembly protetti". Viene spiegato come utilizzare la protezione dall'accesso di codice per migliorare ulteriormente la protezione del codice gestito. Per trarre il massimo vantaggio dal modulo:

Leggere il modulo 6 "Informazioni generali sulla protezione di .NET", per una panoramica e un confronto fra la protezione basata sui ruoli o l'utente e la protezione dall'accesso di codice. Nel modulo 6 sono contenute informazioni di base per il modulo corrente.

Leggere il modulo 7 "Creazione di assembly protetti". Leggere il modulo 7 prima di passare a questo.

Leggere il modulo 9 "Utilizzo della protezione dall'accesso di codice con ASP.NET". Dopo avere letto il modulo corrente, leggere il modulo 9 se si è specificamente interessati ai criteri di protezione dall'accesso di codice e ai livelli di attendibilità in ASP.NET.

Inizio paginaInizio pagina

Spiegazione della protezione dall'accesso di codice

Per utilizzare in maniera efficace la protezione dall'accesso di codice è necessario conoscere le nozioni fondamentali, quali la terminologia e le modalità di valutazione dei criteri. Per ulteriori informazioni di carattere generale relative alla protezione dall'accesso di codice, vedere la sezione "Risorse aggiuntive" alla fine di questo modulo. Se si conosce già la protezione dall'accesso di codice, è possibile saltare questa sezione e passare direttamente alla sezione "APTCA" (AllowPartiallyTrustedCallersAttribute), riportata più avanti in questo modulo.

La protezione dall'accesso di codice è costituita dai seguenti elementi:

Codice

Evidenza

Autorizzazioni

Criteri

Gruppi di codice

Codice

Tutto il codice gestito è soggetto alla protezione dall'accesso di codice. Quando viene caricato un assembly, all'assembly viene concesso un set di autorizzazioni all'accesso di codice che determina il tipo di risorse alle quali l'assembly può accedere e quali tipi di operazioni con privilegi può eseguire. Il sistema di protezione di Microsoft .NET Framework utilizza le prove per autenticare (identificare) il codice al fine di concedere autorizzazioni.

Nota: l'assembly è considerato come l'unità ai fini della configurazione e dell'attendibilità per la protezione dall'accesso di codice. Tutto il codice che fa parte dello stesso assembly riceve le stesse autorizzazioni ed ha pertanto la stessa attendibilità.

Evidenza

Le prove vengono utilizzate dal sistema di protezione di .NET Framework per identificare gli assembly. I criteri di protezione dall'accesso di codice utilizzano le prove come supporto per concedere le autorizzazioni corrette all'assembly corretto. Fra le prove correlate al percorso figurano:

URL. L'URL dal quale è stato ottenuto l'assembly. Si tratta dell'URL base di codici nella sua forma non elaborata, ad esempio, http://webserver/dirv/bin/assembly.dll o file://C:/directory1/directory2/assembly.dll.

Sito. Il sito dal quale è stato ottenuto l'assembly, ad esempio, http://webserver. Il sito deriva dall'URL base di codici.

Directory dell'applicazione. La directory di base per l'applicazione in esecuzione.

Zona. La zona dalla quale è stato ottenuto l'assembly, ad esempio, LocalIntranet o Internet. Anche la zona deriva dall'URL base di codici.

Fra le prove correlate all'autore figurano:

Nome sicuro. Si applica agli assembly con nome sicuro. I nomi sicuri costituiscono un metodo per attribuire una firma digitale a un assembly utilizzando una chiave privata.

Autore. La firma Authenticode, basata sul certificato X.509, utilizzato per la firma del codice, che rappresenta l'organizzazione di sviluppo.

Importante: La prova dell'autore, ossia la firma Authenticode, viene ignorata dall'host ASP.NET, pertanto non può essere utilizzata per configurare i criteri di protezione dall'accesso di codice per le applicazioni Web del lato server. Questa prova viene utilizzata principalmente dall'host di Internet Explorer.

Hash. L'hash è basato sul contenuto globale dell'assembly e consente di rilevare una compilazione particolare di un brano di codice, indipendentemente dal numero di versione. È utile per rilevare quando gli assembly di terze parti vengono modificati (senza un numero di versione aggiornato) e il loro utilizzo non è stato verificato e autorizzato per la build in questione.

Autorizzazioni

Le autorizzazioni rappresentano i diritti di cui il codice dispone per accedere a una risorsa protetta o eseguire un'operazione con privilegi. In .NET Framework sono disponibili le autorizzazioni all'accesso di codice e le autorizzazioni di identità del codice. Nelle autorizzazioni all'accesso di codice è incapsulata la capacità di accedere a una determinata risorsa o eseguire un'operazione con privilegi. Le autorizzazioni di identità del codice vengono utilizzate per limitare l'accesso di codice, in base a un aspetto dell'identità del codice chiamante, ad esempio il nome sicuro.

Le autorizzazioni vengono concesse al codice mediante i criteri di protezione dall'accesso di codice, configurati dall'amministratore. Un assembly può influire, utilizzando le richieste di autorizzazione, anche sul set di autorizzazioni ottenute in ultima analisi. I criteri di protezione dall'accesso di codice e le richieste di autorizzazione determinano congiuntamente le operazioni che il codice può eseguire, ad esempio, per accedere al file system, il codice deve disporre di FileIOPermission e, per accedere al Registro di sistema, di RegistryPermission. Per ulteriori informazioni sulle richieste di autorizzazione, vedere la sezione "Richiesta di autorizzazioni", riportata più avanti in questo modulo.

Nota: si utilizzano i set per raggruppare le autorizzazioni in modo da facilitarne l'amministrazione.

Autorizzazioni limitate e illimitate

Le autorizzazioni possono essere limitate o illimitate. L'autorizzazione FileIOPermission , ad esempio, se illimitata, consente al codice di leggere o scrivere in qualsiasi parte del file system mentre, se limitata, può consentire al codice di leggere i file esclusivamente da una directory specifica.

Richieste

Se per accedere a una risorsa o eseguire un'altra operazione con privilegi si utilizza una classe della libreria di classi di .NET Framework, la classe invia una richiesta di autorizzazione per assicurare che il codice sviluppato, e qualsiasi altro codice che chiama il codice sviluppato, sia autorizzato ad accedere alla risorsa. La richiesta di autorizzazione fa sì che il runtime ripercorra a ritroso lo stack di chiamate (stack frame per stack frame), esaminando le autorizzazioni di tutti i chiamanti compresi nello stack. Se vengono individuati chiamanti che non dispongono delle autorizzazioni richieste, viene generata un'eccezione SecurityException .

Richieste in fase di linking

Nel caso di una richiesta in fase di linking non viene eseguito un controllo completo dello stack, ma solo del chiamante immediato, procedendo a ritroso di uno stack frame nello stack di chiamate. Ne consegue che i rischi di protezione correlati all'utilizzo di richieste in fase di linking sono più elevati. È necessario prestare particolare attenzione agli attacchi di tipo luring.

Nota: con un attacco di tipo luring, il codice nocivo accede alle risorse e operazioni esposte dall'assembly, chiamando il codice attraverso un assembly intermediario attendibile.

Per ulteriori informazioni relative al corretto utilizzo delle richieste in fase di linking, vedere la sezione "Richieste in fase di linking" riportata più avanti in questo modulo.

Metodi Assert, Deny e PermitOnly

Le classi per l'autorizzazione all'accesso di codice supportano i metodi Assert, Deny e PermitOnly. È possibile utilizzare questi metodi per modificare il funzionamento del controllo dello stack di una richiesta di autorizzazione. Vengono definiti modificatori del controllo dello stack.

Una chiamata al metodo Assert fa sì che il controllo dello stack alla ricerca di un'autorizzazione corrispondente si interrompa alla chiamata ad Assert. Spesso questo metodo viene utilizzato per eseguire il codice con privilegi nel sandbox. Per ulteriori informazioni, vedere la sezione "Assert e RevertAssert", riportata più avanti in questo modulo.

Una chiamata al metodo Deny causa il mancato controllo dello stack dal punto in cui viene raggiunta un'autorizzazione corrispondente in poi. Se si chiama codice non attendibile, è possibile utilizzare il metodo Deny per limitare le potenzialità del codice chiamato.

Una chiamata al metodo PermitOnly causa il mancato controllo di uno stack senza corrispondenze. Come avviene per il metodo Deny , si tende a non utilizzarlo spesso, ma può essere impiegato per limitare le azioni di codice non attendibile che potrebbe essere chiamato.

Criteri

I criteri di protezione dall'accesso di codice vengono configurati dagli amministratori e determinano le autorizzazioni concesse agli assembly. È possibile stabilire criteri a quattro livelli:

Organizzazione. Per applicare criteri al livello dell'organizzazione.

Computer. Per applicare criteri al livello del computer.

Utente. Per applicare criteri al livello dell'utente.

Dominio applicazione. Per configurare il dominio applicazione in cui viene caricato un assembly.

In ASP.NET si implementano i criteri del dominio applicazione per consentire la configurazione dei criteri di protezione dall'accesso di codice per le applicazioni e i servizi Web. Per ulteriori informazioni sui criteri del dominio applicazione di ASP.NET, leggere il modulo 9 "Utilizzo della protezione dall'accesso di codice con ASP.NET".

Le impostazioni dei criteri vengono mantenute nei file di configurazione XML. I primi tre livelli di criteri (organizzazione, computer e utente) possono essere configurati mediante lo strumento di configurazione di .NET Framework, contenuto nel gruppo di programmi Strumenti di amministrazione, o l'utilità da riga di comando Caspol.exe. I criteri a livello del dominio applicazione di ASP.NET devono attualmente essere modificati con un editor di testo o XML.

Per ulteriori informazioni sui file dei criteri e i relativi percorsi, vedere il modulo 19 "Protezione dell'applicazione ASP.NET e dei servizi Web".

Gruppi di codice

Ogni file dei criteri contiene un insieme gerarchico di gruppi di codice. I gruppi di codice vengono utilizzati per assegnare le autorizzazioni agli assembly. Un gruppo di codice è costituito da due elementi:

Una condizione di appartenenza . È basata su prove, ad esempio la zona o il nome sicuro di un assembly.

Un set di autorizzazioni. Le autorizzazioni contenute nel set vengono concesse agli assembly la cui prova soddisfa la condizione di appartenenza.

Funzionamento

Nella figura 8.1 è illustrata una panoramica semplificata della protezione dall'accesso di codice.

Vista semplificata della protezione dall'accesso di codice

Figura 8.1
Vista semplificata della protezione dall'accesso di codice

Di seguito è riportato un riepilogo dei passaggi illustrati nella figura 8.1.

1.

Viene caricato un assembly.

Questa operazione viene eseguita da un host di dominio applicazione. In un server Web che carica un assembly di applicazione Web si tratta dell'host ASP.NET.

2.

La prova viene raccolta dall'assembly e presentata dall'host.

3.

La prova viene valutata a fronte dei criteri di protezione definiti.

4.

L'output della valutazione dei criteri di protezione è costituito da uno o più set di autorizzazioni denominati che definiscono le autorizzazioni concesse all'assembly.

Nota: un assembly può includere richieste di autorizzazione, che possono ridurre ulteriormente le autorizzazioni concesse.

5.

Il codice contenuto nell'assembly richiede autorizzazioni corrette per accedere a una risorsa limitata o eseguire un'operazione con privilegi.

Tutte le classi di base di .NET Framework che accedono a risorse o eseguono operazioni con privilegi contengono le richieste di autorizzazione corrette. La classe FileStream , ad esempio, richiede l'autorizzazioneFileIOPermission, la classe Registry richiede l'autorizzazione RegistryPermission, e così via.

6.

Se all'assembly e relativi chiamanti sono state concesse le autorizzazioni richieste, l'operazione può procedere. In caso contrario, verrà generata un'eccezione di protezione.

Valutazione dei criteri

Quando la prova viene elaborata attraverso il motore dei criteri, l'output è costituito da un set di autorizzazioni che definisce le autorizzazioni concesse a un assembly. La concessione dei criteri viene calcolata a ogni livello della gerarchia dei criteri: organizzazione, computer, utente e dominio applicazione. I criteri concessi a ogni livello vengono quindi uniti mediante un'operazione di intersezione per ottenere i criteri infine concessi. Si utilizza un'intersezione per assicurare che i criteri di livello inferiore nella gerarchia non possano aggiungere autorizzazioni non concesse a livelli superiori. Ciò impedisce la concessione da parte di un singolo utente o dominio applicazione di ulteriori autorizzazioni non concesse dall'amministratore dell'organizzazione.

Nella figura 8.2 è illustrato il funzionamento dell'operazione di intersezione, che assicura che la concessione di autorizzazioni risultante sia determinata da tutti i livelli di criteri della gerarchia.

Intersezione dei criteri fra tutti i livelli

Figura 8.2
Intersezione dei criteri fra tutti i livelli

Dalla figura 8.2 si evince che l'operazione di intersezione assicura che solo le autorizzazioni concesse a tutti i livelli vadano a fare parte delle autorizzazioni infine concesse.

Influenza delle richieste di autorizzazione sulla concessione dei criteri

È possibile aggiungere all'assembly attributi di protezione per specificarne i requisiti di autorizzazione. È possibile specificare che all'assembly debba essere concesso il set minimo di autorizzazioni perché possa essere eseguito, ma ciò non influisce sulle autorizzazioni effettivamente concesse. È altresì possibile specificare le autorizzazioni facoltative, non strettamente necessarie, che possono essere utilizzate dall'assembly e quali autorizzazioni si desidera rifiutare. Le autorizzazioni rifiutate sono quelle da non concedere mai all'assembly, neanche se consentite dai criteri di protezione.

Se si richiedono autorizzazioni facoltative, la combinazione di autorizzazioni facoltative e minime diviene oggetto di un'operazione di intersezione con le autorizzazioni concesse dai criteri, che va ulteriormente a ridurle. Le autorizzazioni specificamente rifiutate vengono rimosse nei criteri. Questi concetti sono riepilogati dalla formula riportata di seguito, dove AC corrisponde ai criteri concessi dall'amministratore, definiti criteri di protezione e Amin, Afac e Arifiutate sono le richieste di autorizzazione aggiunte all'assembly dallo sviluppatore.

Autorizzazioni concesse risultanti = (AC ∩ (Amin U Afac)) - Arifiutate

Per ulteriori informazioni relative all'utilizzo delle richieste di autorizzazione, le loro implicazioni e quando utilizzarle, vedere la sezione "Richiesta di autorizzazioni", riportata più avanti in questo modulo.

Valutazione dei criteri a livello dei criteri

Ogni singolo file dei criteri a ogni livello specifico consiste in una disposizione gerarchica di gruppi di codice. Questi gruppi di codice includono le condizioni di appartenenza utilizzate per determinare a quali assembly si applicano e quali set di autorizzazioni vengono utilizzati per determinare le autorizzazioni che devono essere concesse agli assembly corrispondenti. La struttura gerarchica consente di assegnare a un assembly vari set di autorizzazioni e permette ai criteri di protezione di supportare la logica AND e OR semplice. Si considerino ad esempio i criteri di protezione illustrati nella figura 8.3.

Gruppi di codice gerarchici a un singolo livello di criteri

Figura 8.3
Gruppi di codice gerarchici a un singolo livello di criteri

Nota: il gruppo di codice Codice tutti è un gruppo di codice speciale che corrisponde a tutti gli assembly. Costituisce il livello principale dei criteri di protezione e in sé non concede autorizzazioni, in quanto è associato al set di autorizzazioni chiamato Nulla.

Si considerino le autorizzazioni concesse in base ai criteri di protezione illustrati nella figura 8.3.

A tutti gli assembly che traggono origine da Zona_Computer_Locale (assembly installati a livello locale) vengono concesse le autorizzazioni definite dal set FullTrust. Si tratta di un set di autorizzazioni predefinito all'installazione di .NET Framework e rappresenta il set illimitato di tutte le autorizzazioni.

Agli assembly il cui autore è Società1 e che traggono origine dalla zona intranet vengono concesse le autorizzazioni definite dal set di autorizzazioni predefinito Zona_IntranetLocale e dal set di autorizzazioni SetASoc1 personalizzato.

Agli assembly il cui autore è Società2 vengono concesse le autorizzazioni definite dal set di autorizzazioni SetASoc2 personalizzato.

Agli assembly scaricati da a.b.c.com vengono concesse le autorizzazioni definite dal set di autorizzazioni SetAABC personalizzato.

Nota: se non viene soddisfatta la condizione di appartenenza di un determinato gruppo di codice non viene valutato alcun gruppo figlio.

Gruppi di codice esclusivi e di livello finale

L'elaborazione e l'attraversamento della gerarchia di criteri possono essere ottimizzati utilizzando due attributi specificati a livello del gruppo di codice, entrambi impostabili mediante lo strumento di configurazione di .NET Framework. Sono:

Esclusivo
Indica che questo gruppo non può essere unito ad altri gruppi di codice di pari livello. Per contrassegnare un gruppo di codice come Esclusivo si seleziona Il livello dei criteri disporrà solo delle autorizzazioni relative al set associato a questo gruppo di codice nello strumento di configurazione di .NET Framework.

Livello finale
Indica che i criteri di livello inferiore devono essere ignorati. Per contrassegnare un gruppo di codice come Livello finale si seleziona I livelli dei criteri al di sotto di questo livello non verranno valutati nello strumento di configurazione di .NET Framework. Se, ad esempio, un gruppo di codice corrispondente è contrassegnato con Livello finale nei criteri del computer, le impostazioni contenute nel file dei criteri dell'utente verranno ignorate.

Nota: i criteri a livello di dominio applicazione, ad esempio i criteri ASP.NET per le applicazioni Web del lato server, vengono sempre valutati, a prescindere dall'impostazione del livello finale.

Inizio paginaInizio pagina

APTCA

Un assembly che dispone di un nome sicuro non può essere chiamato da un assembly parzialmente attendibile, ossia non totalmente attendibile, a meno che l'assembly con il nome sicuro non contenga l'attributo AllowPartiallyTrustedCallersAttribute (APTCA) come indicato di seguito:

[assembly: AllowPartiallyTrustedCallersAttribute()]

Si tratta di una strategia per diminuire i rischi, volta ad assicurare che il codice sviluppato non possa essere inavvertitamente esposto a codice con attendibilità parziale e quindi potenzialmente nocivo. Ai tipi contenuti in un assembly con nome sicuro Common Language Runtime aggiunge, senza avvisare, una richiesta in fase di linking per il set di autorizzazioni FullTrust in tutti i membri pubblicamente accessibili. Includendo l'attributo APTCA si inibisce la richiesta in fase di linking.

Evitare l'utilizzo dell'attributo APTCA

Se si utilizza l'attributo APTCA il codice è più sensibile agli attacchi e ne consegue che è particolarmente importante riesaminarlo per verificare che sia privo di vulnerabilità di protezione. Utilizzare l'attributo APTCA solo se strettamente necessario.

Nel contesto di un'applicazione Web del lato server, utilizzare l'attributo APTCA solo quando l'assembly deve supportare chiamanti con attendibilità parziale. Questa situazione può verificarsi nelle seguenti circostanze:

L'assembly deve essere chiamato da un'applicazione Web con attendibilità parziale. Si tratta di applicazioni il cui livello <trust> non è impostato come totale. Per ulteriori informazioni sulle applicazioni Web con attendibilità parziale e l'utilizzo dell'attributo APTCA in questa situazione, vedere il modulo 9 "Utilizzo della protezione dall'accesso di codice con ASP.NET".

L'assembly deve essere chiamato da un altro assembly al quale sono state concesse autorizzazioni limitate dall'amministratore della protezione dall'accesso di codice.

L'assembly deve essere chiamato da un altro assembly che rifiuta autorizzazioni specifiche utilizzando SecurityAction.RequestRefuse o SecurityAction.RequestOptional. Ciò fa dell'assembly chiamante un assembly con attendibilità parziale.

L'assembly deve essere chiamato da un altro assembly che utilizza un modificatore del controllo dello stack (ad esempio Deny o PermitOnly) per limitare il codice a valle.

Diagnostica dei problemi relativi all'attributo APTCA

Se si tenta di chiamare un assembly con nome sicuro non contrassegnato con l'attributo APTCA da codice con attendibilità parziale, quale un'applicazione Web, verrà visualizzata un'eccezione simile a quella illustrata nella Figura 8.4. Si noti che nell'eccezione non sono forniti dettagli relativi alle autorizzazioni, ma è semplicemente indicato che le autorizzazioni richieste (in questo caso FullTrust) non possono essere acquisite dall'assembly chiamante. In questo caso il testo della descrizione poco chiaro significa che l'errore si è verificato perché il livello <trust> dell'applicazione era impostato diversamente da totale.

Risultato della chiamata a un assembly dotato di nome sicuro da parte di codice con attendibilità parziale

Figura 8.4
Risultato della chiamata a un assembly dotato di nome sicuro da parte di codice con attendibilità parziale

Per superare questa eccezione è necessario concedere FullTrust al codice chiamante o assegnare l'attributo APTCA all'assembly chiamato. Si noti che per i singoli tipi che fanno parte di un assembly con attributo APTCA potrebbero comunque essere richiesti chiamanti con attendibilità totale, in quanto nei tipi potrebbe essere inclusa una richiesta in fase di linking implicita o una normale richiesta di attendibilità totale, come illustrato negli esempi riportati di seguito.

[PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
[PermissionSet(SecurityAction.Demand, Unrestricted=true)]
Inizio paginaInizio pagina

Codice con privilegi

Quando si progettano e si costruiscono assembly, è necessario essere in grado di identificare il codice con privilegi. Questo ha implicazioni notevoli per la protezione dall'accesso di codice. Il codice con privilegi è codice gestito che accede a risorse protette o esegue altre operazioni delicate per la protezione, ad esempio chiamare codice non gestito o utilizzare la serializzazione o la reflection. Il codice con privilegi dispone di questi privilegi in quanto la protezione dall'accesso di codice deve concedergli autorizzazioni specifiche perché possa funzionare.

Risorse con privilegi

Le risorse con privilegi per le quali il codice richiede autorizzazioni specifiche di protezione dall'accesso di codice sono indicate nella Tabella 8.1.

Tabella 8.1 Risorse protette e autorizzazioni associate

Risorsa protettaAutorizzazione necessaria

Accesso ai dati

SqlClientPermission
OleDbPermission
OraclePermission
Nota: il provider OLE DB di ADO.NET e quelli gestiti da Oracle attualmente richiedono l'attendibilità totale.

Servizi directory

DirectoryServicesPermission

Database DNS

DnsPermission

Registro eventi

EventLogPermission

Variabili di ambiente

EnvironmentPermission

File system

FileIOPermission

Archiviazione isolata

IsolatedStoragePermission

Code di messaggi

MessageQueuePermission

Contatori prestazioni

PerformanceCounterPermission

Stampanti

PrinterPermission

Registro di sistema

RegistryPermission

Socket

SocketPermission

Servizi Web (e altre risorse Internet HTTP)

WebPermission

Operazioni con privilegi

Le operazioni con privilegi sono riportate nella Tabella 8.2, unitamente alle autorizzazioni associate richieste dal codice chiamante.

Tabella 8.2 Operazioni con privilegi e autorizzazioni associate

OperazioneAutorizzazione necessaria

Creazione e controllo dei domini applicazioni

SecurityPermission con
SecurityPermissionFlag.ControlAppDomain

Specificazione dei domini applicazioni dei criteri

SecurityPermission con
SecurityPermissionFlag.ControlDomainPolicy

Asserzione delle autorizzazioni di protezione

SecurityPermission con
SecurityPermissionFlag.Assertion

Creazione e gestione delle prove

SecurityPermission con
SecurityPermissionFlag.ControlEvidence

Creazione e gestione degli oggetti identità

SecurityPermission con
SecurityPermissionFlag.ControlPrincipal

Configurazione dei tipi e servizi remoti dei canali

SecurityPermission con
SecurityPermissionFlag.RemotingConfiguration

Gestione dei criteri di protezione

SecurityPermission con
SecurityPermissionFlag.ControlPolicy

Serializzazione

SecurityPermission con
SecurityPermissionFlag.SerializationFormatter

Operazioni di threading

SecurityPermission con
SecurityPermissionFlag.ControlThread

Reflection

ReflectionPermission

Chiamate a codice non gestito

SecurityPermission con
SecurityPermissionFlag.UnmanagedCode

Inizio paginaInizio pagina

Richiesta di autorizzazioni

Quando si progettano e si sviluppano assembly, creare un elenco di tutte le risorse alle quali il codice accede e di tutte le operazioni con privilegi eseguite dal codice. Al momento della distribuzione, queste informazioni potranno essere utili all'amministratore per configurare adeguatamente i criteri di protezione dall'accesso di codice e per diagnosticare problemi correlati alla protezione.

Il modo migliore per comunicare i requisiti di autorizzazione del codice consiste nell'utilizzare attributi di protezione dichiarativi a livello di assembly per specificare i requisiti minimi. Di norma questi vengono inseriti in Assemblyinfo.cs o Assemblyinfo.vb. Ciò consente all'amministratore o utente dell'assembly di controllare quali autorizzazioni l'assembly richiede utilizzando lo strumento Permview.exe.

RequestMinimum

È possibile utilizzare il metodo SecurityAction.RequestMinimum unitamente ad attributi di autorizzazione dichiarativi per specificare il set minimo di autorizzazioni necessario per l'esecuzione dell'assembly. Se l'assembly necessita, ad esempio, di accedere al Registro di sistema, ma solo per recuperare dati di configurazione da una chiave specifica, utilizzare un attributo simile a quanto riportato di seguito:

[assembly: RegistryPermissionAttribute(
                     SecurityAction.RequestMinimum, 
                     Read=@"HKEY_LOCAL_MACHINE\SOFTWARE\YourApp")]

Se si è certi a priori che il codice verrà eseguito in ambiente con attendibilità totale e che disporrà del set di autorizzazioni completo e illimitato, utilizzare RequestMinimum è di minore importanza. È tuttavia buona norma specificare i requisiti di autorizzazione dell'assembly.

Nota: gli attributi di autorizzazione accettano un elenco delimitato da virgole di proprietà e valori di proprietà dopo gli argomenti obbligatori del costruttore. Questi vengono utilizzati per inizializzare l'oggetto autorizzazione sottostante. Un modo rapido per individuare i nomi di proprietà supportati consiste nell'utilizzare Ildasm.exe sull'assembly contenente il tipo dell'attributo di autorizzazione.

RequestOptional

Se si utilizza il metodo SecurityAction.RequestOptional , all'assembly non verranno concesse altre autorizzazioni se non quelle specificate con SecurityAction.RequestMinimum e SecurityAction.RequestOptional , nonostante avrebbero potuto esserne concesse altre in base ai criteri di protezione.

RequestRefused

SecurityAction.RequestRefuse consente di avere la certezza che all'assembly non possano essere concesse autorizzazioni non necessarie da parte dei criteri di protezione dall'accesso di codice. Se l'assembly, ad esempio, non richiama codice non gestito, è possibile utilizzare l'attributo indicato di seguito per assicurare che i criteri di protezione dall'accesso di codice non concedano all'assembly l'autorizzazione al codice non gestito.

[assembly: SecurityPermissionAttribute(SecurityAction.RequestRefuse, 
                                       UnmanagedCode=true)]

Implicazioni derivanti dall'utilizzo di RequestOptional o RequestRefuse

Se si utilizza RequestOptional, viene effettuata un'intersezione fra il set di autorizzazioni specificate con RequestOptional e RequestMinimum , e le autorizzazioni concesse all'assembly in base ai criteri. Vale a dire che tutte le altre autorizzazioni dell'assembly che non rientrano nei set RequestOptional e RequestMinimum vengono rimosse. Se si utilizza RequestRefuse vengono inoltre rimosse le richieste rifiutate.

Se si utilizza RequestOptional o RequestRefuse, l'assembly diviene pertanto un assembly ad attendibilità parziale, il che si ripercuote sulla chiamata ad altri assembly. Nella scelta tra utilizzare SecurityAction.RequestOptional o SecurityAction.RequestRefuse possono essere d'aiuto le seguenti considerazioni:

Non utilizzarli se si ha la necessità di chiamare direttamente un assembly con nome sicuro senza AllowPartiallyTrustedCallersAttribute (APTCA) in quanto il loro utilizzo ne impedisce la chiamata.

Molti assembly di .NET Framework con nome sicuro contengono tipi che non supportano chiamanti con attendibilità parziale e non includono l'attributo APTCA. Per ulteriori informazioni e un elenco degli assembly che supportano chiamanti con attendibilità parziale, vedere "Sviluppo di applicazioni con attendibilità parziale" nel modulo 9 "Utilizzo della protezione dall'accesso di codice con ASP.NET".

Se si devono effettuare chiamate ad assembly con nome sicuro senza APTCA, informare gli amministratori che installano il codice sviluppato del fatto che, per funzionare correttamente, a tale codice deve essere concessa l'attendibilità totale mediante i criteri di protezione dall'accesso di codice.

Se non è necessario accedere ad assembly APTCA, aggiungere richieste di autorizzazione per rifiutare le autorizzazioni notoriamente non necessarie per l'assembly. Eseguire il test del codice in una fase precoce per verificare che tali autorizzazioni non siano realmente necessarie.

Se il codice a valle necessita delle autorizzazioni rifiutate, un metodo compreso fra il codice sviluppato e quello a valle dovrà effettuare un'asserzione per l'autorizzazione. In caso contrario verrà generata un'eccezione SecurityException quando il controllo dello stack arriverà al codice sviluppato.

Inizio paginaInizio pagina

Autorizzazione del codice

La protezione dall'accesso di codice consente di autorizzare il codice che chiama l'assembly. Viene così ridotto il rischio che codice nocivo possa chiamare il codice sviluppato. È ad esempio possibile utilizzare le autorizzazioni di identità per limitare il codice chiamante in base alla prova dell'identità, ad esempio il componente chiave pubblica del relativo nome sicuro. È altresì possibile utilizzare richieste di autorizzazione all'accesso di codice esplicite per assicurare che il codice che chiama l'assembly disponga delle autorizzazioni necessarie per accedere alla risorsa o eseguire l'operazione con privilegi esposta dall'assembly.

Di solito non si richiedono esplicitamente le autorizzazioni all'accesso di codice. Queste richieste vengono effettuate direttamente dalle classi di .NET Framework e non è necessario duplicarle. Esistono tuttavia occasioni in cui occorre inviare richieste esplicite, ad esempio se il codice sviluppato espone una risorsa personalizzata utilizzando codice non gestito o se accede a dati memorizzati nella cache. Sono disponibili le seguenti modalità di autorizzazione del codice:

Limitare il codice in grado di chiamare il codice sviluppato.

Limitare l'ereditarietà.

Prendere in considerazione la protezione dei dati memorizzati nella cache.

Proteggere le risorse personalizzate con autorizzazioni personalizzate.

Limitare il codice in grado di chiamare il codice sviluppato.

Un metodo contrassegnato come public può essere chiamato da qualsiasi codice esterno all'assembly corrente. Per limitare ulteriormente altro codice in grado di chiamare i metodi utilizzati è possibile ricorrere a una richiesta di autorizzazione dell'identità di protezione dall'accesso di codice, come illustrato nell'esempio riportato di seguito.

public sealed class Utility
{
  // Although SomeOperation() is a public method, the following 
  // permission demand means that it can only be called by assemblies 
  // with the specified public key. 
  [StrongNameIdentityPermission(SecurityAction.LinkDemand, 
                                PublicKey="00240000048...97e85d098615")]
  public static void SomeOperation() {}
}

Nel codice sopra riportato è presente una richiesta in fase di linking. Ne consegue l'autorizzazione del chiamante immediato. Il codice sviluppato risulterebbe pertanto potenzialmente soggetto agli attacchi di tipo luring, nei quali un assembly nocivo potrebbe accedere a risorse o operazioni protette, fornite dal proprio assembly attraverso un intermediario attendibile avente il nome sicuro specificato.

A seconda della natura e delle funzionalità fornite dalla classe, per il codice chiamante potrebbe essere necessario richiedere un'ulteriore autorizzazione rispetto alla richiesta in fase di linking basata sull'identità. È in alternativa possibile prendere in considerazione di utilizzare una richiesta completa unitamente a StrongNameIdentityPermission, sebbene per questo si presuma che tutto il codice incluso nello stack di chiamate sia firmato con un nome sicuro utilizzando la stessa chiave privata.

Nota: l'invio di una richiesta di controllo dell'intero stack per StrongNameIdentityPermission non funziona se l'assembly viene chiamato da un'applicazione o servizio Web. Questo perché non è possibile attribuire un nome sicuro alle classi compilate dinamicamente associate ad applicazioni o servizi Web ASP.NET.

Per estrarre una chiave pubblica da un assembly

Eseguire il comando riportato di seguito per ottenere da un assembly una rappresentazione esadecimale di una chiave pubblica:

secutil -hex -strongname yourassembly.dll

Per estrarre la chiave pubblica da un file con una coppia di chiavi

1.

Generare il file con la coppia di chiavi utilizzando il comando riportato di seguito:

sn -k keypairfile

2.

Estrarre la chiave pubblica dal file con la coppia di chiavi:

sn -p keypairfile publickeyfile

3.

Ottenere una rappresentazione esadecimale della chiave pubblica:

sn -tp publickeyfile < publickeyhex.dat

Limitare l'ereditarietà

Se la classe è progettata come classe base, è possibile limitare il codice che può derivarne utilizzando una richiesta di ereditarietà unitamente a StrongNameIdentityPermission , come illustrato nell'esempio riportato di seguito. Ciò impedisce che assembly che non dispongono della chiave privata corrispondente alla chiave pubblica contenuta nella richiesta ereditino dalla classe.

// The following inheritance demand ensures that only code within the 
// assembly with the specified public key (part of the assembly's strong
// name can sub class SomeRestrictedClass
[StrongNameIdentityPermission(SecurityAction.InheritanceDemand,
                              PublicKey="00240000048...97e85d098615")]
public class SomeRestrictedClass
{
}

Prendere in considerazione la protezione dei dati memorizzati nella cache

Se si accede a una risorsa utilizzando una delle classi di .NET Framework, la classe invia una richiesta di autorizzazione corretta per il tipo di risorsa in questione. Se in seguito i dati vengono memorizzati nella cache per ragioni di prestazioni, è opportuno prendere in considerazione di inviare una richiesta di autorizzazione all'accesso di codice esplicita prima di accedere ai dati contenuti nella cache. Ciò assicura che il codice chiamante sia autorizzato ad accedere al tipo di risorsa specifico. Se, ad esempio, i dati vengono prima letti da un file e in seguito memorizzati nella cache, e si desidera assicurare che il codice chiamante sia autorizzato, inviare una richiesta FileIOPermission , come illustrato nell'esempio riportato di seguito.

// The following demand assumes the cached data was originally retrieved from
// C:\SomeDir\SomeFile.dat
new FileIOPermission(FileIOPermissionAccess.Read, 
                     @"C:\SomeDir\SomeFile.dat").Demand();
// Now access the cache and return the data to the caller

Proteggere le risorse personalizzate con autorizzazioni personalizzate

Se si espone una risorsa o operazione utilizzando codice non gestito, è opportuno eseguire il codice wrapper nel sandbox e prendere in considerazione di richiedere un'autorizzazione personalizzata per autorizzare il codice chiamante.

Ai chiamanti con attendibilità totale l'autorizzazione personalizzata viene concessa automaticamente, purché il tipo di autorizzazione implementi l'interfaccia IUnrestrictedPermission. I chiamanti con attendibilità parziale non disporranno dell'autorizzazione a meno che questa non venga specificamente concessa dai criteri di protezione dall'accesso di codice. Viene così assicurato che il codice non attendibile non possa chiamare l'assembly per accedere alla risorsa personalizzata da questo esposta. Il sandboxing implica inoltre che non si è costretti a concedere la potente autorizzazione UnmanagedCodePermission al codice che deve chiamare il codice sviluppato.

Per ulteriori informazioni sulle chiamate a codice non gestito, vedere la sezione "Codice non gestito", riportata più avanti in questo modulo. Per un esempio di implementazione di un'autorizzazione personalizzata, vedere "Procedura: Creare un'autorizzazione per la crittografia personalizzata" nella sezione "Procedure" di questa guida.

Inizio paginaInizio pagina

Richieste in fase di linking

Una richiesta in fase di linking differisce da una normale richiesta di autorizzazione in quanto il runtime richiede autorizzazioni esclusivamente al chiamante immediato e non esegue un controllo completo dello stack. Le richieste in fase di linking vengono effettuate al momento della compilazione JIT e possono essere specificate solo in modo dichiarativo.

Prima di utilizzare una richiesta in fase di linking è necessario fare tutte le considerazioni del caso, in quanto il loro utilizzo può causare vulnerabilità. Se si utilizzano richieste in fase di linking prendere in considerazione i seguenti problemi:

Attacchi di tipo luring

Prestazioni e richieste in fase di linking

Metodi chiamanti e richieste in fase di linking

Utilizzo congiunto di richieste in fase di linking a livello di classe e di metodo

Interfacce e richieste in fase di linking

Strutture e richieste in fase di linking

Metodi virtuali e richieste in fase di linking

Attacchi di tipo luring

Se si protegge il codice con una richiesta in fase di linking, sarà vulnerabile agli attacchi di tipo luring, nei quali il codice dannoso riesce ad accedere alla risorsa o operazione esposta dal codice sviluppato attraverso un intermediario attendibile, come illustrato nella Figura 8.5.

Esempio di attacco di tipo luring con richieste in fase di linking

Figura 8.5
Esempio di attacco di tipo luring con richieste in fase di linking

Nella Figura 8.5 i metodi contenuti nell'assembly X, che accede a una risorsa protetta, sono protetti con una richiesta in fase di linking di una chiave pubblica specifica che utilizza un'autorizzazione StrongNameIdentityPermission). Gli assembly A, B e C sono firmati mediante una chiave privata corrispondente alla chiave pubblica considerata attendibile per l'assembly X, pertanto questi assembly possono chiamare l'assembly X. Gli assembly A, B e C sono soggetti a un attacco di tipo luring se i chiamanti non vengono controllati alla ricerca di una prova specifica prima di effettuare chiamate all'assembly X. L'assembly D, ad esempio, che non è firmato con la stessa chiave privata, non può chiamare direttamente l'assembly X. Potrebbe tuttavia accedere all'assembly X attraverso l'assembly attendibile A, se A non controlla i propri chiamanti attraverso un'altra richiesta in fase di linking o una richiesta completa.

Utilizzare le richieste in fase di linking in un assembly esclusivamente quando i chiamanti dell'assembly non ne espongono ulteriormente le funzionalità, ad esempio se il chiamante è un'applicazione e non una libreria, o quando si è certi che sia sufficiente verificare l'identità del chiamante immediato con una richiesta di autorizzazione dell'identità.

Prestazioni e richieste in fase di linking

Rispetto ad altri problemi di prestazioni di applicazioni Web, quali la latenza della rete e l'accesso al database, l'impatto del controllo dello stack è contenuto. Non utilizzare le richieste in fase di linking esclusivamente per ottenere un vantaggio a livello di prestazioni. Le richieste complete offrono un livello di protezione molto più elevato.

Metodi chiamanti e richieste in fase di linking

Se si chiama un metodo protetto di richiesta in fase di linking, per effetto di tale richiesta verrà controllato solo il codice sviluppato. In questa situazione è opportuno accertarsi che tale codice adotti misure adeguate per autorizzare i chiamanti, ad esempio la richiesta di un'autorizzazione.

Utilizzo congiunto di richieste in fase di linking a livello di classe e di metodo

Le richieste in fase di linking a livello di metodo hanno la precedenza rispetto a quelle a livello di classe. Nel brano di codice riportato di seguito, ad esempio, la richiesta in fase di linking di FileIOPermission deve essere ripetuta nella dichiarazione del metodo, altrimenti la richiesta in fase di linking di EnvironmentPermission sostituirà quella a livello di classe di FileIOPermission.

[FileIOPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public sealed class SomeClass
{
  // The unrestricted FileIOPermission link demand must be restated at the
  // method level, if the method is decorated with another link demand.
  // Failure to do so means that (in this example) that the 
  // EnvironmentPermission link demand would override the class level
  // FileIOPermission link demand
  [FileIOPermission(SecurityAction.LinkDemand, Unrestricted=true)]
  [EnvironmentPermission(SecurityAction.LinkDemand, Read="PATH")]
  public void SomeMethod()
  {
  }
}

Interfacce e richieste in fase di linking

Se la classe implementa un'interfaccia e una delle implementazioni dei metodi contiene una richiesta in fase di linking, prestare attenzione affinché nella dichiarazione del metodo contenuta nella definizione dell'interfaccia sia presente la stessa richiesta in fase di linking. In caso contrario, per ignorare la richiesta in fase di linking, il chiamante dovrà semplicemente chiamare il metodo attraverso l'interfaccia. Di seguito è riportato un esempio.

public interface IMyInterface
{
  // The link demand shown on the method implementation below 
  // should be repeated here
  void Method1();
}

public class MyImplementation : IMyInterface
{
  // The method implementation has a link demand but the interface does not
  [SecurityPermission(SecurityAction.LinkDemand, 
            Flags=SecurityPermissionFlag.ControlPrincipal)]
  public void Method1()
  {
  }
}

Utilizzando il codice riportato di seguito, il chiamante è soggetto alla richiesta in fase di linking:

MyImplementation t = new MyImplementation();
t.Method1();

Utilizzando il codice riportato di seguito, il chiamante non è soggetto alla richiesta in fase di linking:

IMyInterface i = new MyImplementation();
i.Method1();

Strutture e richieste in fase di linking

Le richieste in fase di linking non impediscono la costruzione di strutture da parte di chiamanti non attendibili. Questo avviene perché i costruttori predefiniti non vengono generati automaticamente per le strutture. La richiesta in fase di linking a livello di struttura si applica pertanto solo se si utilizza un costruttore esplicito. Ad esempio:

[SecurityPermission(SecurityAction.LinkDemand, 
           Flags=SecurityPermissionFlag.ControlPrincipal)]
public struct SomeStruct
{
  // This explicit constructor is protected by the link demand
  public SomeStruct(int i)
  {
    field = i;
  }
  private int field;
}

Le due righe di codice riportate di seguito hanno entrambe come esito una nuova struttura con il campo inizializzato a zero. Solo la prima riga, che utilizza il costruttore esplicito, è tuttavia soggetta a una richiesta in fase di linking.

SomeStruct s = new SomeStruct(0);
SomeStruct s = new SomeStruct();	

La seconda riga non è soggetta a una richiesta in fase di linking perché non viene generato alcun costruttore predefinito. Se si trattasse di una classe anziché di una struttura, il compilatore genererebbe un costruttore predefinito contenente un'annotazione relativa alla richiesta in fase di linking specificata.

Metodi virtuali e richieste in fase di linking

Se si utilizza una richiesta in fase di linking per proteggere l'override di un metodo in una classe derivata, avere cura di inserirla anche nel corrispondente metodo della classe base virtuale. In caso contrario non verrebbe eseguita alcuna richiesta in fase di linking qualora il compilatore JIT individuasse un riferimento al tipo della classe base e non fosse presente una richiesta in fase di linking.

Inizio paginaInizio pagina

Assert e RevertAssert

È possibile chiamare il metodo CodeAccessPermission.Assert per evitare che una richiesta si propaghi oltre il frame dello stack corrente. Utilizzando Assert si garantisce l'attendibilità dei chiamanti del codice. Data la possibilità di attacchi di tipo luring, Assert deve essere utilizzato con cautela.

Lo si utilizza più spesso per eseguire il codice con privilegi nel sandbox. Se si sviluppa codice che chiama Assert, è necessario assicurare che siano in atto misure di protezione alternative per l'autorizzazione del codice chiamante. I consigli seguenti contribuiscono a ridurre i rischi al minimo.

Utilizzare lo schema richiesta/asserzione

Ridurre la durata di Assert

Utilizzare lo schema richiesta/asserzione

Richiedere un'autorizzazione specifica prima di chiamare Assert è un modo efficace per autorizzare il codice a monte. A volte può essere possibile richiedere un tipo di autorizzazione predefinito per autorizzare il codice chiamante.

Spesso se l'assembly espone funzionalità non disponibili nella libreria di classi di .NET Framework, ad esempio la chiamata a DPAPI (Data Protection API), è necessario sviluppare un'autorizzazione personalizzata e richiederla per autorizzare i chiamanti. Si potrebbe ad esempio sviluppare l'autorizzazione personalizzata Crittografia per autorizzare i chiamanti a un assembly wrapper DPAPI gestito. Richiedere questa autorizzazione e quindi effettuare l'asserzione dell'autorizzazione del codice non gestito costituisce un modo efficace per autorizzare il codice chiamante.

Per ulteriori informazioni su questo tipo di approccio e lo sviluppo di autorizzazioni personalizzate, vedere "Procedura: creare un'autorizzazione per la crittografia personalizzata" nella sezione "Procedure" di questa guida.

Ridurre la durata di Assert

Se è necessario chiamare Assert solo per soddisfare le richieste di un solo metodo a valle chiamato dal codice, inserire Assert immediatamente prima della chiamata al metodo a valle, quindi chiamare immediatamente RevertAssert per mantenere al minimo le dimensioni della finestra di asserzione e per assicurare che il codice seguente chiamato dal metodo non venga inavvertitamente eseguito senza errori perché Assert è ancora valido.

È prassi comune inserire la chiamata a RevertAssert in un blocco finally per assicurare che venga sempre eseguita anche nel caso sia generata un'eccezione.

Inizio paginaInizio pagina

Limitazione del codice

Limitare e costruire codice con privilegi minimi equivale ad adottare il principio dei privilegi minimi quando si configura un account utente o di servizio. Limitando le autorizzazioni di protezione dall'accesso di codice per il codice che si sviluppa si riducono al minimo le probabilità che possa essere utilizzato in maniera dannosa.

Sono disponibili due modi per limitare il codice in modo da restringere le risorse alle quali può accedere e le altre operazioni con privilegi che può eseguire:

Utilizzo della concessione di autorizzazioni basate su criteri

Utilizzo di modificatori del controllo dello stack

Utilizzo della concessione di autorizzazioni basate su criteri

È possibile configurare i criteri di protezione dall'accesso di codice per concedere a un determinato assembly un set di autorizzazioni limitato. Se ne contiene così la capacità di accedere alle risorse o eseguire altre operazioni con privilegi. Per ulteriori informazioni, vedere"Procedura: utilizzo dei criteri di protezione dall'accesso di codice per vincolare un assembly" nella sezione "Procedure" di questa guida.

Utilizzo di modificatori del controllo dello stack

È possibile utilizzare modificatori del controllo dello stack per assicurare che siano disponibili solo autorizzazioni specifiche per il codice chiamato. È ad esempio possibile utilizzare SecurityAction.PermitOnly per assicurare che il metodo sviluppato e gli altri metodi chiamati dispongano esclusivamente di un set di autorizzazioni limitato. Nell'esempio riportato di seguito viene applicato un set di autorizzazioni molto restrittivo. Il codice dispone solo dell'autorizzazione all'esecuzione, non può accedere alle risorse o eseguire altre operazioni con privilegi.

[SecurityPermissionAttribute(SecurityAction.PermitOnly, 
                             Flags=SecurityPermissionFlag.Execution)]
public void SomeMethod()
{	
  // The current method and downstream can only execute. They cannot access 
  // resources or perform other privileged operations.
  SomeOtherMethod();
}

Nelle sezioni riportate di seguito viene spiegato come utilizzare la protezione dall'accesso di codice per limitare l'accesso a vari tipi di risorse, compresi l'I/O di file, l'accesso al registro eventi o Registro di sistema, ai dati, ai servizi directory, variabili di ambiente, servizi Web e socket.

Inizio paginaInizio pagina

I/O di file

Per poter eseguire l'I/O di file, i criteri di protezione dall'accesso di codice devono concedere all'assembly l'autorizzazione FileIOPermission. Se viene concessa l'autorizzazione illimitata FileIOPermission, il codice potrà accedere ai file ovunque si trovino nel sistema, in base alla protezione di Windows. È possibile utilizzare un'autorizzazione FileIOPermission limitata per ridurre la capacità di un assembly di eseguire l'I/O di file, specificando ad esempio i diritti di accesso consentiti (lettura, lettura/scrittura e così via).

Limitazione dell'I/O di file nel contesto dell'applicazione

Un requisito comune è la capacità di limitare l'I/O di file a percorsi di directory specifici, quali la gerarchia di directory dell'applicazione.

Nota: se l'applicazione Web è configurata con Attendibilità media, l'accesso ai file è automaticamente limitato alla gerarchia di directory virtuali dell'applicazione. Per ulteriori informazioni, leggere il modulo 9 "Utilizzo della protezione dall'accesso di codice con ASP.NET".

Configurare l'applicazione con Attendibilità media costituisce un modo per limitare l'I/O di file, nonostante questa limitazione riduca la capacità dell'applicazione di accedere ad altri tipi di risorse. Esistono altri due modi per limitare le capacità di I/O di file del codice:

Utilizzo di PermitOnly per limitare l'I/O di file

Configurazione dei criteri di protezione dall'accesso di codice per limitare l'I/O di file

Utilizzo di PermitOnly per limitare l'I/O di file

Per limitare l'I/O di file è possibile utilizzare attributi dichiarativi unitamente a SecurityAction.PermitOnly , come illustrato nell'esempio riportato di seguito.

// Allow the code only to read files from c:\YourAppDir
[FileIOPermission(SecurityAction.PermitOnly, Read=@"c:\YourAppDir\")]
[FileIOPermission(SecurityAction.PermitOnly, PathDiscovery=@"c:\YourAppDir\")]
public static string ReadFile(string filename)
{
  // Use Path.GetFilePath() to canonicalize the file name
  // Use FileStream.OpenRead to open the file
  // Use FileStream.Read to access and return the data
}

Nota: il secondo attributo che specifica l'accesso PathDicovery è necessario per la funzione Path.GetFilePath, utilizzata per normalizzare il nome del file di input.

Per evitare di codificare come hard coded la gerarchia di directory dell'applicazione, è possibile utilizzare la sintassi imperativa per la protezione e ricorrere a HttpContext.Current.Request.MapPath(".") per recuperare la directory dell'applicazione Web in fase di runtime. È necessario fare riferimento all'assembly System.Web e aggiungere la corrispondente istruzione using , come illustrato nell'esempio riportato di seguito.

using System.Web;

public static string ReadFile(string filename)
{
  string appDir = HttpContext.Current.Request.MapPath(".");
  FileIOPermission f = new FileIOPermission(PermissionState.None);
  f.SetPathList(FileIOPermissionAccess.Read, appDir);
  f.SetPathList(FileIOPermissionAccess.PathDiscovery, appDir);
  f.PermitOnly();

  // Use Path.GetFilePath() to canonicalize the file name
  // Use FileStream.OpenRead to open the file
  // Use FileStream.Read to access and return the data
}

Nota: per un'applicazione Windows è possibile sostituire la chiamata a MapPath con una chiamata a Directory.GetCurrentDirectory per ottenere la directory corrente dell'applicazione.

Configurazione dei criteri di protezione dall'accesso di codice per limitare l'I/O di file

Un amministratore potrà inoltre configurare i criteri di protezione dall'accesso di codice in modo da limitare la capacità del codice di eseguire l'I/O di file oltre la gerarchia di directory virtuali dell'applicazione.

L'amministratore potrebbe, ad esempio, configurare i criteri di protezione dall'accesso di codice a livello di organizzazione o computer in modo da concedere all'assembly un'autorizzazione FileIOPermission limitata. Questo risultato è più facilmente ottenibile se l'assembly contiene un nome sicuro, in quanto l'amministratore potrà avvalersi di questa prova crittograficamente solida nel configurare i criteri. Per gli assembly che non dispongono di un nome sicuro si dovrà utilizzare una forma di prova alternativa. Per ulteriori informazioni su come configurare la protezione dall'accesso di codice per limitare la capacità di I/O di file di un assembly, vedere "Procedura: Utilizzo dei criteri di protezione dall'accesso di codice per vincolare un assembly" nella sezione "Procedure" di questa guida.

Se l'assembly viene chiamato da un'applicazione Web, un approccio migliore consiste nel configurare i criteri di protezione dall'accesso di codice di ASP.NET a livello del dominio applicazione in modo da poter sfruttare $AppDirUrl$, che rappresenta la directory principale virtuale dell'applicazione. Per ulteriori informazioni sulla limitazione dell'I/O di file utilizzando i criteri di protezione dall'accesso di codice di ASP.NET, leggere il modulo 9 "Utilizzo della protezione dall'accesso di codice con ASP.NET."

Richiesta di FileIOPermission

Come supporto per l'amministratore, se al momento della build sono noti i requisiti precisi dell'assembly per l'I/O dei file, come ad esempio i nomi delle directory, dichiarare FileIOPermission utilizzando una richiesta di autorizzazione dichiarativa, come illustrato nell'esempio riportato di seguito.

[assembly: FileIOPermission(SecurityAction.RequestMinimum, Read=@"C:\YourAppDir")]

L'amministratore potrà vedere questo attributo utilizzando permview.exe. L'ulteriore vantaggio derivante dall'utilizzo di SecurityAction.RequestMinimum è il mancato caricamento dell'assembly nel caso le autorizzazioni non siano sufficienti, preferibile rispetto alla generazione di un'eccezione di protezione del runtime.

Inizio paginaInizio pagina

Registro eventi

Per poter accedere al registro eventi, i criteri di protezione dall'accesso di codice devono concedere all'assembly l'autorizzazione EventLogPermission. Se ciò non avviene, ad esempio perché è in esecuzione nel contesto di un'applicazione Web con attendibilità media, è necessario eseguire il codice di registrazione eventi nel sandbox. Per ulteriori informazioni sull'esecuzione dell'accesso al registro eventi nel sandbox, leggere il modulo 9 "Utilizzo della protezione dall'accesso di codice con ASP.NET."

Limitazione del codice di registrazione eventi

Se si desidera limitare le azioni del codice wrapper del registro eventi, magari scritto da un altro sviluppatore o organizzazione, è possibile utilizzare attributi dichiarativi unitamente a SecurityAction.PermitOnly, come illustrato nell'esempio riportato di seguito.

L'attributo indicato nell'esempio assicura che il metodo WriteToLog e tutti gli altri metodi da esso chiamati possano accedere esclusivamente al registro eventi del computer locale e non possano cancellare registri o origini degli eventi. Queste operazioni non sono consentite da EventLogPermissionAccess.Instrument.

[EventLogPermission(SecurityAction.PermitOnly, 
                    MachineName=".",
                    PermissionAccess=EventLogPermissionAccess.Instrument)]
public static void WriteToLog( string message )

Per attivare l'accesso in sola lettura ai registri esistenti utilizzare EventLogPermissionAccess.Browse.

Richiesta di EventLogPermission

Per documentare i requisiti di autorizzazione del codice e assicurare che l'assembly non possa essere caricato se non dispone di diritti di accesso al registro eventi sufficienti in base a quanto stabilito dai criteri di protezione dall'accesso di codice, aggiungere un attributo EventLogPermissionAttribute a livello dell'assembly, con SecurityAction.RequestMinimum , come illustrato nell'esempio riportato di seguito.

// This attribute indicates that your code requires the ability to access the
// event logs on the local machine only (".") and needs instrumentation access
// which means it can read or write to existing logs and create new event sources 
// and event logs
[assembly: EventLogPermissionAttribute(SecurityAction.RequestMinimum,
                                       MachineName=".",
                                       PermissionAccess=
                                       EventLogPermissionAccess.Instrument)]
Inizio paginaInizio pagina

Registro di sistema

I criteri di protezione dall'accesso di codice devono concedere l'autorizzazione RegistryPermission al codice che accede al Registro di sistema utilizzando la classe Microsoft.Win32.Registry. Questo tipo di autorizzazione può essere utilizzato per limitare l'accesso al Registro di sistema a chiavi e sottochiavi specifiche, oltre che per controllare la capacità del codice di leggere, scrivere o creare chiavi di registro e valori denominati.

Limitazione dell'accesso al Registro di sistema

Per limitare il codice alla lettura di dati da chiavi specifiche del Registro di sistema è possibile utilizzare RegistryPermissionAttribute unitamente a SecurityAction.PermitOnly. L'attributo indicato di seguito assicura che il codice possa leggere esclusivamente dalla chiave YourApp e relative sottochiavi in HKEY_LOCAL_MACHINE\SOFTWARE.

[RegistryPermissionAttribute(SecurityAction.PermitOnly,
                             Read=@"HKEY_LOCAL_MACHINE\SOFTWARE\YourApp")]
public static string GetConfigurationData( string key, string namedValue )
{
  return (string)Registry.
                 LocalMachine.
                 OpenSubKey(key).
                 GetValue(namedValue);
}

Richiesta di RegistryPermission

Per documentare i requisiti di autorizzazione del codice e assicurare che l'assembly non possa essere caricato se non dispone di diritti di accesso al Registro di sistema sufficienti in base a quanto stabilito dai criteri di protezione dall'accesso di codice, aggiungere un attributo RegistryPermissionAttribute a livello dell'assembly, con SecurityAction.RequestMinimum , come illustrato nell'esempio riportato di seguito.

[assembly: RegistryPermissionAttribute(SecurityAction.RequestMinimum,
                                   Read=@"HKEY_LOCAL_MACHINE\SOFTWARE\YourApp")]
Inizio paginaInizio pagina

Accesso ai dati

Il provider di dati SQL Server di ADO.NET supporta i chiamanti con attendibilità parziale. Gli altri provider di dati, compresi provider OLE DB, Oracle e ODBC, attualmente richiedono chiamanti con attendibilità totale.

Se per la connessione a SQL Server viene utilizzato il provider di dati SQL Server, per il codice di accesso ai dati è necessaria l'autorizzazione SqlClientPermission. È possibile utilizzare SqlClientPermission per limitare l'intervallo consentito di coppie nome/valore che è possibile utilizzare nelle stringhe di connessione passate all'oggetto SqlConnection . Nel codice riportato di seguito, il metodo CheckProductStockLevel è stato potenziato con un ulteriore controllo di protezione per assicurare che non sia possibile evitare di utilizzare una password nella stringa di connessione. Se il codice recupera una stringa di connessione senza password viene generata un'eccezione SecurityException .

[SqlClientPermissionAttribute(SecurityAction.PermitOnly, 
                              AllowBlankPassword=false)]
public static int CheckProductStockLevel(string productCode)
{
  // Retrieve the connection string from the registry
  string connectionString = GetConnectionString();
  . . .
}

Per ulteriori informazioni sull'esecuzione del codice di accesso ai dati nel sandbox in modo che i provider di dati OLE DB ed altri possano essere utilizzati da applicazioni Web con attendibilità parziale, vedere il modulo 9 "Utilizzo della protezione dall'accesso di codice con ASP.NET."

Inizio paginaInizio pagina

Servizi directory

Il codice che utilizza classi dello spazio dei nomi System.DirectoryServices per accedere a servizi directory quali Active Directory deve attualmente disporre dell'attendibilità totale. È tuttavia possibile utilizzare DirectoryServicesPermission per limitare il tipo di accesso e i servizi directory specifici ai quali il codice può connettersi.

Limitazione dell'accesso ai servizi directory

Per limitare il codice è possibile utilizzare DirectoryServicesPermissionAttribute unitamente a SecurityAction.PermitOnly. L'attributo indicato di seguito assicura che il codice possa connettersi esclusivamente a un percorso LDAP specifico e sfogliare la directory.

[DirectoryServicesPermissionAttribute(SecurityAction.PermitOnly, 
                       Path="LDAP://rootDSE",
                       PermissionAccess=DirectoryServicesPermissionAccess.Browse)]
public static string GetNamingContext(string ldapPath)
{
  DirectorySearcher dsSearcher = new DirectorySearcher(ldapPath);
  dsSearcher.PropertiesToLoad.Add("defaultNamingContext");
  dsSearcher.Filter = "";
  SearchResult result = dsSearcher.FindOne();
  return (string)result.Properties["adsPath"][0];
}

Richiesta di DirectoryServicesPermission

Per documentare i requisiti di autorizzazione del codice e assicurare che l'assembly non possa essere caricato se non dispone di diritti di accesso ai servizi directory sufficienti in base a quanto stabilito dai criteri di protezione dall'accesso di codice, aggiungere un attributo DirectoryServicesPermissionAttribute a livello dell'assembly, con SecurityAction.RequestMinimum , come illustrato nell'esempio riportato di seguito.

[assembly: DirectoryServicesPermissionAttribute(SecurityAction.RequestMinimum, 
                       Path="LDAP://rootDSE",
                       PermissionAccess=DirectoryServicesPermissionAccess.Browse)]
Inizio paginaInizio pagina

Variabili di ambiente

I criteri di protezione dall'accesso di codice devono concedere l'autorizzazione EnvironmentPermission al codice che necessita di leggere o scrivere variabili di ambiente utilizzando la classe System.Environment. Questo tipo di autorizzazione può essere utilizzato per limitare l'accesso a specifiche variabili di ambiente denominate.

Limitazione dell'accesso alle variabili di ambiente

Per limitare il codice in modo che possa leggere esclusivamente determinate variabili di ambiente è possibile utilizzare EnvironmentPermissionAttribute unitamente a SecurityAction.PermitOnly. Gli attributi indicati di seguito assicurano che il codice possa leggere solo dalle variabili username, userdomain e temp.

[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="username")]
[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="userdomain")]
[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="temp")]
public static string GetVariable(string name)
{
  return Environment.GetEnvironmentVariable(name);
}

Richiesta di EnvironmentPermission

Per documentare i requisiti di autorizzazione del codice e assicurare che l'assembly non possa essere caricato se non dispone di diritti di accesso alle variabili di ambiente sufficienti in base a quanto stabilito dai criteri di protezione dall'accesso di codice, aggiungere un attributo EnvironmentPermissionAttribute a livello dell'assembly, con SecurityAction.RequestMinimum , come illustrato nell'esempio riportato di seguito.

[assembly: EnvironmentPermissionAttribute(SecurityAction.RequestMinimum,
                                          Read="username"),
           EnvironmentPermissionAttribute(SecurityAction.RequestMinimum,
                                          Read="userdomain"),
           EnvironmentPermissionAttribute(SecurityAction.RequestMinimum,
                                          Read="temp")]
Inizio paginaInizio pagina

Servizi Web

I criteri di protezione dall'accesso di codice devono concedere al codice che chiama servizi Web l'autorizzazione WebPermission. WebPermission limita effettivamente l'accesso a tutte le risorse HTTP basate su Internet.

Limitazione delle connessioni ai servizi Web

Per limitare i servizi Web ai quali il codice può accedere utilizzare WebPermissionAttribute unitamente a SecurityAction.PermitOnly. Il codice seguente, ad esempio, assicura che il metodo PlaceOrder e gli altri metodi da esso chiamati possano richiamare solo i servizi Web del sito http://host.

[WebPermissionAttribute(SecurityAction.PermitOnly,
                        ConnectPattern=@"http://somehost/.*")]
[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="USERNAME")]
public static void PlaceOrder(XmlDocument order)
{
  PurchaseService.Order svc = new PurchaseService.Order();
  // Web service uses Windows authentication
  svc.Credentials = System.Net.CredentialCache.DefaultCredentials;
  svc.PlaceOrder(order);
}

Nell'esempio riportato in precedenza viene utilizzata la proprietà ConnectPattern della classe WebPermissionAttribute. Ciò consente di fornire un'espressione regolare corrispondente all'intervallo di indirizzi con i quali è possibile stabilire una connessione. L'attributo EnvironmentPermissionAttribute indicato in precedenza è necessario perché il codice utilizza l'autenticazione di Windows e le credenziali predefinite.

Nell'esempio seguente viene illustrato come utilizzare l'attributo Connect per limitare le connessioni a un determinato servizio Web.

[WebPermissionAttribute(SecurityAction.PermitOnly,
                        Connect=@"http://somehost/order.asmx")]
Inizio paginaInizio pagina

Socket e DNS

I criteri di protezione dall'accesso di codice devono concedere l'autorizzazione SocketPermission al codice che utilizza direttamente i socket avvalendosi della classe System.Net.Sockets.Socket . Se inoltre il codice utilizza DNS per mappare i nomi degli host agli indirizzi IP, dovrà disporre dell'autorizzazione DnsPermission.

È possibile utilizzare SocketPermission per limitare l'accesso a porte specifiche di host specifici. È altresì possibile limitare la possibilità di utilizzare il socket per accettare connessioni o iniziare connessioni in uscita, oltre che limitare il protocollo di trasporto, ad esempio TCP (Transmission Control Protocol) o UDP (User Datagram Protocol).

Limitazione dell'accesso ai socket

Per far sì che il codice possa utilizzare i socket esclusivamente in modo limitato è possibile utilizzare SocketPermissionAttribute unitamente a SecurityAction.PermitOnly. Gli attributi indicati di seguito assicurano che il codice possa connettersi esclusivamente a una porta o host specifico utilizzando il protocollo TCP. Dato che per risolvere i nomi host il codice chiama Dns.Resolve, sarà necessaria anche l'autorizzazione DnsPermission.

[SocketPermissionAttribute(SecurityAction.PermitOnly, 
                           Access="Connect", 
                           Host="hostname", 
                           Port="80", 
                           Transport="Tcp")]
[DnsPermissionAttribute(SecurityAction.PermitOnly, Unrestricted=true)]
public string MakeRequest(string hostname, string message)
{
  Socket socket = null;
  IPAddress serverAddress = null;
  IPEndPoint serverEndPoint = null;
  byte[] sendBytes = null, bytesReceived = null;
  int bytesReceivedSize = -1, readSize = 4096;

  serverAddress = Dns.Resolve(hostname).AddressList[0];
  serverEndPoint = new IPEndPoint(serverAddress, 80);
  socket = new Socket(AddressFamily.InterNetwork, 
                      SocketType.Stream, ProtocolType.Tcp);
  bytesReceived = new byte[readSize];
  sendBytes = Encoding.ASCII.GetBytes(message);
  socket.Connect(serverEndPoint);
  socket.Send(sendBytes);
  bytesReceivedSize = socket.Receive(bytesReceived, readSize, 0);
  socket.Close();
  if(-1 != bytesReceivedSize)
  {
    return Encoding.ASCII.GetString(bytesReceived, 0, bytesReceivedSize);
  }
  return "";
}

Richiesta di SocketPermission e DnsPermission

Per documentare i requisiti di autorizzazione del codice e assicurare che l'assembly non possa essere caricato se non dispone di diritti di accesso ai socket o DNS in base a quanto stabilito dai criteri di protezione dall'accesso di codice, aggiungere un attributo SocketPermissionAttribute e un attributo DnsPermissionAttribute a livello dell'assembly, con SecurityAction.RequestMinimum, come illustrato nell'esempio riportato di seguito.

[assembly: SocketPermissionAttribute(SecurityAction.RequestMinimum, 
                                     Access="Connect", 
                                     Host="hostname", 
                                     Port="80", 
                                     Transport="Tcp")
           DnsPermissionAttribute(SecurityAction.PermitOnly, Unrestricted=true)]
Inizio paginaInizio pagina

Codice non gestito

Il codice che chiama API Win32 non gestite o componenti COM richiede l'autorizzazione al codice non gestito, che dovrebbe essere concessa esclusivamente a codice altamente attendibile. Viene definita dal tipo SecurityPermission con la proprietà Flags impostata a SecurityPermissionFlag.UnmanagedCode.

Le linee guida per chiamare codice non gestito riportate di seguito sono complementari a quelle presentate nel modulo 7 "Creazione di assembly protetti."

Utilizzare convenzioni di denominazione per indicare il rischio .

Richiedere l'autorizzazione al codice non gestito.

Eseguire le chiamate ad API non gestite nel sandbox.

Utilizzare SupressUnmanagedCodeSecurityAttribute con cautela.

Utilizzare convenzioni di denominazione per indicare il rischio.

Classificare il codice non gestito e prevedere i tipi utilizzati per incapsulare le API non gestite servendosi delle convenzioni di denominazione riportate di seguito.

Protetto. Identifica il codice che non pone problemi di protezione. Può essere chiamato da qualsiasi codice, sia dannoso sia inoffensivo. Ne è un esempio il codice che restituisce il conteggio della frequenza corrente del processore. Alle classi protette è possibile aggiungere l'attributo SuppressUnmanagedCode che disattiva la richiesta di autorizzazione alla protezione dall'accesso di codice in caso di attendibilità totale.

[SuppressUnmanagedCode]
class SafeNativeMethods {
       [DllImport("user32")]
       internal static extern void MessageBox(string text);
}

Nativo. Si tratta di un tipo di codice non gestito potenzialmente pericoloso, ma protetto con una richiesta di controllo completo dello stack per l'autorizzazione al codice non gestito. Questo avviene implicitamente al livello dell'interoperatività, a meno che il codice non gestito non sia stato soppresso dall'attributo SupressUnmanagedCode.

class NativeMethods {
       [DllImport("user32")]
       internal static extern void FormatDrive(string driveLetter);
}

Non affidabile. Si tratta di codice non gestito potenzialmente pericoloso con richiesta di protezione per l'autorizzazione al codice non gestito soppressa in maniera dichiarativa. Questi metodi possono essere pericolosi. Tutti i chiamanti di questi metodi devono essere sottoposti a un controllo completo della protezione per assicurare che il loro utilizzo sia protetto, in quanto non viene effettuato alcun controllo dello stack.

[SuppressUnmanagedCodeSecurity]
class UnsafeNativeMethods {
       [DllImport("user32")]
       internal static extern void CreateFile(string fileName);
}

Richiedere l'autorizzazione al codice non gestito

Con nome sicuro

[assembly: SecurityPermission(SecurityAction.RequestMinimum, 
                              UnmanagedCode=true)]

Eseguire le chiamate ad API non gestite nel sandbox.

Isolare le chiamata a codice non gestito in assembly specifici e mantenere al minimo il numero di assembly che chiamano codice non gestito. Utilizzare quindi lo schema del sandboxing per assicurare che l'autorizzazione al codice non gestito venga concessa solo a determinati assembly.

Per eseguire nel sandbox codice gestito che chiama codice non gestito

1.

Inserire il codice che chiama codice non gestito in un assembly distinto (wrapper).

2.

Aggiungere un nome sicuro all'assembly.
Ciò consente di applicare con facilità all'assembly i criteri di protezione dall'accesso di codice personalizzati. Per ulteriori informazioni, vedere la sezione "Nomi sicuri" del modulo 7 "Creazione di assembly protetti".

3.

Richiedere l'autorizzazione al codice non gestito, come descritto nella sezione riportata in precedenza.

4.

Autorizzare il codice chiamante con una richiesta di autorizzazione completa.
Di solito è necessario utilizzare un'autorizzazione personalizzata che rappresenta la risorsa non gestita esposta dall'assembly. Ad esempio:

(new EncryptionPermission(EncryptionPermissionFlag.Encrypt, 
                          storePermissionFlag.Machine)).Demand();

5.

Effettuare un'asserzione dell'autorizzazione al codice non gestito nella classe wrapper:

(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert();

Per un esempio di implementazione completo che illustra come chiamare la funzionalità DPAPI Win32 non gestita, vedere "Procedura: creare un'autorizzazione per la crittografia personalizzata" nella sezione "Procedure" di questa guida.

Utilizzare SupressUnmanagedCodeSecurity con cautela

Se l'assembly effettua chiamate a codice non gestito, il sovraccarico associato alla gestione di molte richieste di autorizzazione al codice non gestito può ripercuotersi sulle prestazioni.

In questo caso è possibile utilizzare l'attributo SupressUnmanagedCodeSecurity nella dichiarazione del metodo P/Invoke. Così facendo la richiesta completa dell'autorizzazione al codice non gestito viene sostituita da una richiesta in fase di linking, effettuata una sola volta al momento della compilazione JIT.

Come avviene quando si utilizzano richieste in fase di linking, il codice diviene vulnerabile ad attacchi di tipo luring. Per ridurre il rischio, ricorrere alla soppressione della richiesta di autorizzazione al codice non gestito solo quando nell'assembly si adottano precauzioni adeguate per assicurare che non possa essere forzato ad eseguire operazioni indesiderate. Un esempio di contromisura adeguata è costituito dalla richiesta da parte dell'assembly di un'autorizzazione personalizzata che rispecchi più da vicino le operazioni eseguite dal codice non gestito.

Utilizzare SuppressUnmanagedCodeSecurity con P/Invoke

Il codice riportato di seguito illustra come applicare l'attributo SuppressUnmanagedCodeSecurity alla dichiarazione del metodo P/Invoke (Platform Invocation Services).

public NativeMethods
{
  // The use of SuppressUnmanagedCodeSecurity here applies only to FormatMessage
  [DllImport("kernel32.dll"), SuppressUnmanagedCodeSecurity]
  private unsafe static extern int FormatMessage(
                                      int dwFlags, 
                                      ref IntPtr lpSource, 
                                      int dwMessageId,
                                      int dwLanguageId, 
                                      ref String lpBuffer, int nSize, 
                                      IntPtr *Arguments);
}

Utilizzare SuppressUnmanagedCodeSecurity con l'interoperabilità COM

Per le chiamate di interoperabilità COM, l'attributo deve essere utilizzato a livello dell'interfaccia, come illustrato nell'esempio riportato di seguito.

[SuppressUnmanagedCodeSecurity]
public interface IComInterface
{
}
Inizio paginaInizio pagina

Delegati

Non esiste un modo per sapere a priori quali operazioni verranno svolte da un metodo delegato quando lo si richiama. Se l'assembly supporta chiamanti ad attendibilità parziale, è necessario adottare precauzioni aggiuntive quando si richiama un delegato. Per migliorare ulteriormente la protezione si può ricorrere alla protezione dall'accesso di codice.

Prendere in considerazione di limitare le autorizzazioni del delegato .

Non effettuare l'asserzione di un'autorizzazione prima di chiamare un delegato.

Prendere in considerazione di limitare le autorizzazioni del delegato

Le autorizzazioni concesse al codice che chiama il delegato determinano le potenzialità di quest'ultimo. Se il codice sviluppato dispone di maggiori autorizzazioni rispetto al codice che fornisce il delegato, ciò fornisce al chiamante un appiglio per poter eseguire codice con autorizzazioni elevate. Per risolvere il problema è possibile autorizzare il codice esterno, nel punto in cui viene passato il delegato, mediante un'adeguata richiesta di autorizzazione o limitare le autorizzazioni del delegato immediatamente prima di chiamarlo, utilizzando un modificatore dello stack di tipo "deny" o "permit only". Il codice seguente, ad esempio, concede al codice delegato solo l'autorizzazione all'esecuzione per limitarne le potenzialità.

// Delegate definition
public delegate void SomeDelegate();
. . . 
// Permit only execution, prior to calling the delegate. This prevents the
// delegate code accessing resources or performing other privileged
// operations
new SecurityPermission(SecurityPermissionFlag.Execution).PermitOnly();
// Now call the "constrained" delegate
SomeDelegate();
// Revert the permit only stack modifier
CodeAccessPermission.RevertPermitOnly();

Non effettuare l'asserzione di un'autorizzazione prima di chiamare un delegato

È pericoloso effettuare un'asserzione di un'autorizzazione prima di chiamare un delegato, in quanto non è noto il livello di attendibilità del codice che verrà eseguito quando si richiama il delegato. Il codice che passa il delegato fa parte dello stack di chiamate e può pertanto essere controllato mediante una richiesta di protezione adeguata. Non esistono tuttavia modi che consentano di conoscere il livello di attendibilità o le autorizzazioni concesse al codice delegato in sé.

Per ulteriori informazioni sull'utilizzo protetto dei delegati , vedere la sezione "Delegati" del modulo 7 "Creazione di assembly protetti".

Inizio paginaInizio pagina

Serializzazione

Al codice che supporta la serializzazione deve essere concessa l'autorizzazione SecurityPermission con
l'attributo Flag impostato a SerializationFormatter. Se si sviluppano classi che supportano la serializzazione e il codice supporta chiamanti con attendibilità parziale, è opportuno prendere in considerazione di utilizzare richieste di autorizzazione aggiuntive per imporre limitazioni al tipo di codice che può serializzare lo stato dell'oggetto.

Limitazione della serializzazione

Se si crea una classe che implementa l'interfaccia ISerializable , la quale consente la serializzazione dell'oggetto, è possibile aggiungere una richiesta di autorizzazione all'implementazione di ISerializable.GetObjectData per autorizzare il codice che tenta di serializzare l'oggetto. Questo è particolarmente importante se il codice supporta chiamanti con attendibilità parziale.

Il seguente brano di codice, ad esempio, utilizza una richiesta StrongNameIdentityPermission per assicurare che solo il codice firmato con una determinata chiave privata corrispondente alla chiave pubblica contenuta nella richiesta possa serializzare lo stato dell'oggetto.

[StrongNameIdentityPermission(SecurityAction.Demand, 
                              PublicKey="00240000048...97e85d098615")]
public override void GetObjectData(SerializationInfo info, 
                                   StreamingContext context)

Per ulteriori informazioni sull'utilizzo protetto della serializzazione, vedere la sezione "Serializzazione" del modulo 7 " Creazione di assembly protetti".

Inizio paginaInizio pagina

Riepilogo

La protezione dall'accesso di codice consente di limitare le operazioni che il codice può eseguire, oltre che il tipo di codice autorizzato a chiamare il codice sviluppato, nonché di identificare il codice. In ambienti con attendibilità totale, in cui il codice sviluppato e il codice chiamante dispongono di un set di autorizzazioni complete, la protezione dall'accesso di codice è di minore importanza.

Se il codice supporta chiamanti con attendibilità parziale i rischi di protezione sono molto maggiori. In scenari caratterizzati dall'attendibilità parziale, la protezione dall'accesso di codice consente di ridurre alcuni dei rischi aggiuntivi e di limitare il codice con privilegi.

Inizio paginaInizio pagina

Risorse aggiuntive

Per ulteriori informazioni, vedere le risorse seguenti:

"Security in .NET: The Security Infrastructure of the CLR Provides Evidence, Policy, Permissions, and Enforcement Services" di Don Box, in MSDN Magazine di settembre 2002, all'indirizzo http://msdn.microsoft.com/msdnmag (in inglese).

"Security in .NET: Enforce Code Access Rights with the Common Language Runtime" di Keith Brown, in MSDN Magazine di febbraio 2001, all'indirizzo http://msdn.microsoft.com/msdnmag (in inglese).

.NET Framework Security di LaMacchia, Lange, Lyons, Martin e Price, pubblicato da Addison Wesley (in inglese).


Inizio paginaInizio pagina