Chaînes d’objectif dans ASP.NET Core

Les composants qui consomment IDataProtectionProvider doivent transmettre un paramètre purposes unique à la méthode CreateProtector. Le paramètre purposes est inhérent à la sécurité du système de protection des données, car il fournit une isolation entre les consommateurs de chiffrement, même si les clés de chiffrement racine sont les mêmes.

Lorsqu’un consommateur spécifie un objectif, la chaîne d’objectif est utilisée avec les clés de chiffrement racine pour dériver des sous-clés de chiffrement propres à ce consommateur. Cela permet d’isoler le consommateur de tous les autres consommateurs de chiffrement dans l’application : aucun autre composant ne peut lire ses charges utiles et il ne peut pas lire les charges utiles des autres composants. Cette isolation rend également infaisables des catégories entières d’attaques contre le composant.

Purpose Diagram Example

Dans le diagramme ci-dessus, chacune des instances IDataProtector A et B ne peut pas lire les charges utiles de l’autre, mais uniquement les siennes.

La chaîne d’objectif n’a pas besoin d’être secrète. Elle doit simplement être unique dans le sens où aucun autre composant au comportement normal ne fournira jamais la même chaîne d’objectif.

Conseil

L’utilisation de l’espace de noms et du nom de type du composant qui consomme les API de protection des données est une bonne règle générale, car dans la pratique, ces informations ne seront jamais en conflit.

Un composant créé par Contoso et responsable de la création de jetons du porteur peut utiliser Contoso.Security.BearerToken comme chaîne d’objectif. Ou, mieux encore, il peut utiliser Contoso.Security.BearerToken.v1 comme chaîne d’objectif. L’ajout du numéro de version permet à une version ultérieure d’utiliser Contoso.Security.BearerToken.v2 comme objectif. Ainsi, les différentes versions seraient complètement isolées les unes des autres en ce qui concerne les charges utiles.

Puisque le paramètre purposes pour CreateProtector est un tableau de chaînes, le paramètre ci-dessus aurait pu être spécifié comme [ "Contoso.Security.BearerToken", "v1" ] à la place. Cela permet d’établir une hiérarchie d’objectifs et ouvre la possibilité de scénarios multitenants avec le système de protection des données.

Avertissement

Les composants ne doivent pas autoriser l’entrée utilisateur non approuvée à être la seule source d’entrée pour la chaîne d’objectifs.

Prenez par exemple un composant Contoso.Messaging.SecureMessage qui est responsable du stockage des messages sécurisés. Si le composant de messagerie sécurisée devait appeler CreateProtector([ username ]), un utilisateur malveillant peut créer un compte avec le nom d’utilisateur « Contoso.Security.BearerToken » dans une tentative d’amener le composant à appeler CreateProtector([ "Contoso.Security.BearerToken" ]), ce qui amène par inadvertance le système de messagerie sécurisé à créer des charges utiles qui peuvent être perçues comme des jetons d’authentification.

Une meilleure chaîne d’objectifs pour le composant de messagerie serait CreateProtector([ "Contoso.Messaging.SecureMessage", $"User: {username}" ]), qui fournit une isolation correcte.

L’isolation fournie par IDataProtectionProvider, IDataProtector et les objectifs, et leurs comportements sont les suivants :

  • Pour un objet IDataProtectionProvider donné, la méthode CreateProtector crée un objet IDataProtector lié de manière unique à l’objet IDataProtectionProvider qui l’a créé et au paramètre purposes qui a été transmis à la méthode.

  • Le paramètre purposes ne doit pas être nul. (Si le paramètre purposes est spécifié en tant que tableau, cela signifie que le tableau doit être de longueur supérieure à zéro et que tous les éléments du tableau doivent être non nuls.) Un objectif de chaîne vide est techniquement autorisé, mais nous vous le déconseillons.

  • Deux arguments purposes sont équivalents si et uniquement s’ils contiennent les mêmes chaînes (comparateur ordinal utilisé) dans le même ordre. Un argument purpose unique équivaut au tableau d’objectifs à élément unique correspondant.

  • Deux objets IDataProtector sont équivalents si et seulement s’ils sont créés depuis des objets IDataProtectionProvider équivalents avec des paramètres purposes équivalents.

  • Pour un objet IDataProtector donné, un appel à Unprotect(protectedData) renvoie l’objet d’origine unprotectedData si et seulement si protectedData := Protect(unprotectedData) pour un objet IDataProtector équivalent.

Remarque

Nous ne prenons pas en compte le cas où un composant choisit intentionnellement une chaîne d’objectif connue pour entrer en conflit avec un autre composant. Un tel composant serait essentiellement considéré comme malveillant. Or, ce système n’est pas destiné à fournir des garanties de sécurité si un code malveillant est déjà en cours d’exécution à l’intérieur du processus Worker.