Sécurité et conditions de concurrence
Le risque d’exploitation de failles de sécurité par des conditions de concurrence représente également un sujet de préoccupation. Ceci peut se produire de différentes manières. Les sous-rubriques qui suivent décrivent certains des principaux pièges que le développeur doit éviter.
Conditions de concurrence dans la méthode Dispose
Si la méthode Dispose d’une classe (pour plus d’informations, consultez Garbage collection) n’est pas synchronisée, le code de nettoyage dans Dispose est susceptible d’être exécuté plusieurs fois comme illustré dans l’exemple suivant.
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;
}
}
Étant donné que cette implémentation Dispose n’est pas synchronisée, Cleanup
peut être appelé par un premier thread, puis un deuxième thread avant que _myObj
soit défini sur null. La question de savoir s’il s’agit d’un problème de sécurité dépend de ce qui se passe quand le code Cleanup
s’exécute. Les implémentations Dispose non synchronisées posent un problème majeur vis-à-vis de l’utilisation de handles de ressources tels que des fichiers. Une suppression incorrecte peut entraîner l’utilisation d’un mauvais handle, ce qui provoque souvent des failles de sécurité.
Conditions de concurrence dans les constructeurs
Dans certaines applications, d’autres threads peuvent parfois accéder aux membres de classe avant que leurs constructeurs de classe n’aient été complètement exécutés. Vous devez passer en revue tous les constructeurs de classe pour prévenir tout problème de sécurité si cela venait à se produire, ou synchroniser les threads si nécessaire.
Conditions de concurrence avec des objets mis en cache
Le code qui met en cache les informations de sécurité ou utilise l’opération de sécurité d’accès du code Assert peut également être vulnérable aux conditions de concurrence si d’autres parties de la classe ne sont pas correctement synchronisées, comme illustré dans l’exemple suivant.
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();
}
}
S’il existe d’autres chemins à DoOtherWork
qui peuvent être appelés à partir d’un autre thread avec le même objet, un appelant non approuvé peut effectuer une demande de manière furtive.
Si votre code met en cache des informations de sécurité, veillez à vérifier sa vulnérabilité à cet égard.
Conditions de concurrence dans les finaliseurs
Des conditions de concurrence peuvent également se produire dans un objet qui référence une ressource statique ou non managée, qu’il libère ensuite dans son finaliseur. Si plusieurs objets partagent une ressource manipulée dans le finaliseur d’une classe, ils doivent synchroniser tous les accès à cette ressource.