Seguridad y condiciones de carrera
Otra área de preocupación es la posibilidad de vulnerabilidades de seguridad aprovechadas por las condiciones de carrera. Esto puede suceder de varias formas. Los subtemas que siguen describen algunos de los principales problemas que debe evitar el desarrollador.
Condiciones de carrera en el método Dispose
Si el método Dispose de una clase (para más información, consulte Recolección de elementos no utilizados) no está sincronizado, es posible que el código de limpieza dentro de Dispose se pueda ejecutar más de una vez, como se muestra en el ejemplo siguiente.
Sub Dispose()
If Not (myObj Is Nothing) Then
Cleanup(myObj)
myObj = Nothing
End If
End Sub
void Dispose()
{
if (myObj != null)
{
Cleanup(myObj);
myObj = null;
}
}
Dado que esta implementación de Dispose no está sincronizada, es posible que un subproceso llame primero a Cleanup
y, a continuación lo haga un segundo subproceso antes de que _myObj
se establezca en null. Si esto supone un problema de seguridad o no depende de lo que suceda cuando se ejecute el código Cleanup
. Un problema importante con las implementaciones de Dispose no sincronizadas implica el uso de identificadores de recursos como archivos. La eliminación incorrecta puede hacer que se use el identificador incorrecto, lo que a menudo conduce a vulnerabilidades de seguridad.
Condiciones de carrera en constructores
En algunas aplicaciones, es posible que otros subprocesos accedan a los miembros de clase antes de que sus constructores de clase se hayan ejecutado por completo. Debe revisar todos los constructores de clase para asegurarse de que no haya ningún problema de seguridad si esto sucediera, o sincronizar los subprocesos si es necesario.
Condiciones de carrera con objetos almacenados en caché
El código que almacena en caché la información de seguridad o usa la operación Assert de seguridad de acceso del código también podría ser vulnerable a las condiciones de carrera si otras partes de la clase no están sincronizadas correctamente, como se muestra en el ejemplo siguiente.
Sub SomeSecureFunction()
If SomeDemandPasses() Then
fCallersOk = True
DoOtherWork()
fCallersOk = False
End If
End Sub
Sub DoOtherWork()
If fCallersOK Then
DoSomethingTrusted()
Else
DemandSomething()
DoSomethingTrusted()
End If
End Sub
void SomeSecureFunction()
{
if (SomeDemandPasses())
{
fCallersOk = true;
DoOtherWork();
fCallersOk = false;
}
}
void DoOtherWork()
{
if (fCallersOK)
{
DoSomethingTrusted();
}
else
{
DemandSomething();
DoSomethingTrusted();
}
}
Si hay otras rutas de acceso a DoOtherWork
a las que se puede llamar desde otro subproceso con el mismo objeto, un llamador que no es de confianza puede deslizarse detrás de una petición.
Si el código almacena en caché la información de seguridad, asegúrese de revisarla para esta vulnerabilidad.
Condiciones de carrera en finalizadores
Las condiciones de carrera también pueden producirse en un objeto que hace referencia a un recurso estático o no administrado que luego libera en su finalizador. Si varios objetos comparten un recurso que se manipula en el finalizador de una clase, los objetos deben sincronizar todos los accesos a ese recurso.