Protezione della gestione delle eccezioni
In Visual C++ e Visual Basic un'espressione di filtro di un livello superiore dello stack viene eseguita prima dell'istruzione finally. Il blocco catch associato a tale filtro viene eseguito dopo l'istruzione finally. Per ulteriori informazioni, vedere Utilizzo di eccezioni filtrate dall'utente. In questa sezione sono esaminate le implicazioni che questo ordine di istruzioni può avere sulla sicurezza. Nell'esempio di pseudo-codice riportato di seguito è illustrato l'ordine di esecuzione delle istruzioni di filtro e delle istruzioni finally.
void Main()
{
try
{
Sub();
}
except (Filter())
{
Console.WriteLine("catch");
}
}
bool Filter () {
Console.WriteLine("filter");
return true;
}
void Sub()
{
try
{
Console.WriteLine("throw");
throw new Exception();
}
finally
{
Console.WriteLine("finally");
}
}
Il codice viene visualizzato nel seguente modo:
Throw
Filter
Finally
Catch
Il filtro viene eseguito prima dell'istruzione finally; problemi di sicurezza possono essere provocati da elementi che determinino un cambiamento di stato che può essere sfruttato tramite l'esecuzione di altro codice. Di seguito è riportato un esempio:
try
{
Alter_Security_State();
// This means changing anything (state variables,
// switching unmanaged context, impersonation, and
// so on) that could be exploited if malicious
// code ran before state is restored.
Do_some_work();
}
finally
{
Restore_Security_State();
// This simply restores the state change above.
}
Questo pseudo-codice consente l'esecuzione di codice arbitrario da parte di un filtro nella parte alta dello stack. Altri esempi di operazioni che possono avere un effetto simile sono la rappresentazione temporanea di un'altra identità, l'impostazione di un flag interno che consente di ignorare un controllo di sicurezza o la modifica della lingua associata al thread. Si consiglia di introdurre un gestore di eccezioni per isolare le modifiche dello stato del thread da parte del codice dai blocchi dei filtri del chiamante. È tuttavia importante che il gestore eccezioni sia introdotto in modo adeguato; in caso contrario, non sarà possibile risolvere il problema. Nell'esempio riportato di seguito viene modificata la lingua dell'interfaccia utente, ma è possibile esporre in modo simile qualsiasi tipo di cambiamento di stato del thread.
YourObject.YourMethod()
{
CultureInfo saveCulture = Thread.CurrentThread.CurrentUICulture;
try {
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");
// Do something that throws an exception.
}
finally {
Thread.CurrentThread.CurrentUICulture = saveCulture;
}
}
Public Class UserCode
Public Shared Sub Main()
Try
Dim obj As YourObject = new YourObject
obj.YourMethod()
Catch e As Exception When FilterFunc
Console.WriteLine("An error occurred: '{0}'", e)
Console.WriteLine("Current Culture: {0}",
Thread.CurrentThread.CurrentUICulture)
End Try
End Sub
Public Function FilterFunc As Boolean
Console.WriteLine("Current Culture: {0}", Thread.CurrentThread.CurrentUICulture)
Return True
End Sub
End Class
La soluzione corretta in questo caso consiste nell'eseguire il wrapping di un blocco try/finally presente in un blocco try/catch. La semplice introduzione di una clausola catch-throw nel blocco try/finally non comporta la correzione del problema, come mostrato nell'esempio riportato di seguito.
YourObject.YourMethod()
{
CultureInfo saveCulture = Thread.CurrentThread.CurrentUICulture;
try
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");
// Do something that throws an exception.
}
catch { throw; }
finally
{
Thread.CurrentThread.CurrentUICulture = saveCulture;
}
}
Il problema non viene risolto in quanto l'istruzione finally non viene eseguita prima che FilterFunc
ottenga il controllo.
Nell'esempio riportato di seguito il problema viene risolto facendo in modo che la clausola finally sia eseguita prima della generazione di un'eccezione nei blocchi dei filtri delle eccezioni del chiamante.
YourObject.YourMethod()
{
CultureInfo saveCulture = Thread.CurrentThread.CurrentUICulture;
try
{
try
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");
// Do something that throws an exception.
}
finally
{
Thread.CurrentThread.CurrentUICulture = saveCulture;
}
}
catch { throw; }
}