Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

CoqBlog

.NET is good :-)
{ Blog de coq }

Actualités

Attribut PrincipalPermission : attention aux effets de "caspol.exe -s off"

Il n'est pas rare, lorsqu'on vient à parler de contrôle d'accès par rôle, de voir des solutions préconisées reposant sur l'attribut PrincipalPermission.
Le code du corps de la méthode suivante n'est censé s'exécuter que si l'utilisateur est membre du groupe "Administrators" :

[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
public void RunSomethingAdministrative()
{
// ...
}
 

Problème : le comportement lié à cet attribut est impacté par la commande "caspol.exe -s off"

Attention : Mes connaissances sur le hosting du CLR ne me permettent pas de dire si un hôte (IIS etc) peut choisir de surcharger cette désactivation. Je ne peux donc pas affirmer que le problème a lieu pour un code hosté par IIS (ASP.NET, WCF, ...), si quelqu'un se sent le courage de tester... ;-)

 

 

caspol -s off ?

Toute personne ayant déjà joué avec le CAS (ou utiliser VSTO dans ses premières versions) connait le switch "-s" de l'outil caspol, qui permet de rendre le CAS inopérant pour les applications .NET lancées après exécution de la commande.

En .NET 1.x, cette commande pouvait être utilisée pour une désactivation d'une durée indéterminée, l'outil caspol terminant son exécution directement (d'où la présence de "-s on" pour réactiver la sécurité).

Avec .NET 2.0, cette commande a été revue afin de mieux affirmer que son existence est uniquement dédiée à des fins de test : à l'exécution caspol désactive la sécurité mais reste ouvert jusqu'à appui sur "Entrée", ce qui a pour effet la réactivation immédiate de la sécurité pour toute nouvelle application lancée par la suite.
Les applications lancées pendant la désactivation restent en mode désactivé, je suppose que la vérification de la présence du mutex se fait à l'initialisation du CLR, pas à l'évaluation des permissions (ce qui serait logique vu les précautions prises).

Malgré le fait que l'implémentation de cette fonctionnalité est moins dangereuse depuis .NET 2.0 car non permanente (enfin, normalement... Dans l'utilisation normale elle ne l'est pas, même en cas d'arrêt brutal de caspol), il n'en reste pas moins qu'elle est globale au système et non pas limitée à la session en cours (bref, sa portée est celle d'un mutex créé avec le préfixe "Global\", voir CreateMutex/CreateMutexEx).

 

 

En quoi cela nous concerne ? 

Tout d'abord, reprécisons bien une chose : même si ils ont la même finalité, il ne faut pas confondre l'attribut PrincipalPermission (classe PrincipalPermissionAttribute) et la classe PrincipalPermission.

L'attribut permet la spécification de manière déclarative :

[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
public void RunSomethingAdministrative()
{
// ...
}

Alors que la classe PrincipalPermission sera utilisée de manière impérative :

public void RunSomethingAdministrative()
{
PrincipalPermission perm = new PrincipalPermission(
null, "Administrators");
perm.Demand();

// Do something
Thread.CurrentThread.Join(1000);
}

 

La première fois que j'ai eu à envisager d'utiliser cet attribut moi même (étonnamment, cette semaine), le fait que PrincipalPermissionAttribute et PrincipalPermission résident dans le namespace System.Security.Permissions m'a tout de suite fait penser à cette fameuse commande de caspol... et à la question "Si je l'exécute, plus aucune validation de rôle ?".

Dans un sens ce serait logique de ne plus avoir de barrière, c'est un peu le comportement attendu de "-s off" tout de même.
Mais dans les faits ce que j'attends (à titre personnel, allez savoir si j'ai raison ou pas) de PrincipalPermissionAttribute/PrincipalPermission est que leur validation soit toujours active, quoi qu'il arrive.

 

La documentation de caspol (dans sa version .NET 2.0) dit :

-s[ecurity] {on | off}
Turns code access security on or off. Specifying the -s off option does not disable role-based security.

Chouette ! Mais vérifions tout de même.

 

Prenons le code suivant :

internal interface IService
{
void RunSomethingAdministrative();
}

/// <summary>
/// Méthode déclarative
/// </summary>
internal class DeclarativePPService : IService
{
[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
public void RunSomethingAdministrative()
{
// Do something
Thread.CurrentThread.Join(1000);
}
}

/// <summary>
/// Méthode impérative avec PrincipalPermission
/// </summary>
internal class ImperativePPService : IService
{
public void RunSomethingAdministrative()
{
PrincipalPermission perm = new PrincipalPermission(
null, "Administrators");
perm.Demand();

// Do something
Thread.CurrentThread.Join(1000);
}
}

/// <summary>
/// Méthode impérative avec levée manuelle d'une exception
/// </summary>
internal class ImperativeCustomService : IService
{
public void RunSomethingAdministrative()
{
this.EnsureAccessGranted();

// Do something
Thread.CurrentThread.Join(1000);
}

private void EnsureAccessGranted()
{
if (!Thread.CurrentPrincipal.IsInRole("Administrators"))
{
throw new AccessDeniedException("Access denied !");
}
}
}

J'ai pris volontairement le cas d'une situation à risque (en l'absence d'emprunt d'identitité/délégation, risque d'élévation de privilège en cas de défaut de validation correcte du rôle de l'appelant) du point de vue technique sur un système, pour lequel on aurait à priori plutôt opté directement pour un mode plus paranoïaque (vérification impérative) et surtout choisi un modèle en délégation (mais des fois ce n'est pas applicable).
Mais le cas d'un rôle métier est tout aussi problématique même si moins grave pour le système au premier abord (cependant on ne sait pas vraiment si à terme ce groupe ne finira pas par disposer de privilèges sur le système en lui même).

Si j'exécute le test suivant dans une situation normale, avec un compte non administrateur :

static void Main(string[] args)
{
Program.RunTest(new DeclarativePPService());
Program.RunTest(new ImperativePPService());
Program.RunTest(new ImperativeCustomService());
}

private static void RunTest(IService implementation)
{
String message = null;

try
{
implementation.RunSomethingAdministrative();
message = "ACCESS GRANTED !";
}
catch (SecurityException ex)
{
message = String.Format("Access denied ! ({0})", ex.Message);
}
catch (AccessDeniedException ex)
{
message = String.Format("Access denied ! ({0})", ex.Message);
}

Console.WriteLine("{0,-23} : {1}",
implementation.GetType().Name, message);
}

Nous obtenons bien, comme prévu :

DeclarativePPService    : Failed ! (Request for principal permission failed.)
ImperativePPService : Failed ! (Request for principal permission failed.)
ImperativeCustomService : Failed ! (Access denied !)

Relancé après désactivation du CAS :

DeclarativePPService    : SUCCESS !
ImperativePPService : Failed ! (Request for principal permission failed.)
ImperativeCustomService : Failed ! (Access denied !)

La vérification par rôle reste bien active dans son mode impératif, pas dans le mode déclaratif.

 

 

Ca peut être bon à retenir, au cas où...

En gros, sur cette permission qualifiable de "non-CAS", nous sommes un peu entre 2 mondes : désactiver le CAS désactive seulement le mode déclaratif, alors que pour les autres permissions la désactivation porte à la fois sur le mode déclaratif et le mode impératif. 

Bref, pas forcément un gros problème vu le peu d'utilisation qui est normalement fait de "caspol -s", mais il serait quand même bon (à mon avis) que les personnes intervenant sur des problèmes de production soient conscientes de cet effet qui ne vient pas forcément à l'esprit immédiatement.
Les conséquences pourraient être désastreuses dans le contexte d'un système fonctionnant non pas par emprunt d'identité/délégation mais sur le modèle "trusted subsystem" ("sous -système approuvé" / "sous-système de confiance").

 

Avec .NET 4.0 la situation devrait être différente vu les changements intervenant autour du CAS. Voir Security Changes in the .NET Framework 4 et Caspol.exe (Code Access Security Policy Tool).

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: dimanche 18 octobre 2009 23:16 par coq
Classé sous : , ,

Commentaires

tja a dit :

Merci pour les détails. Effectivement c'est à avoir à l'esprit quand on joue avec CAS

# octobre 19, 2009 13:05
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01