Dans toute solution d’API management, il faut distinguer la sécurité de façade (celle exposée aux consommateurs de l’API) et la sécurité d’arrière plan (celle présentée par le service exposé au travers d’APIM). L’un des intérêts d’une solution d’API management est notamment de pouvoir décorréler ces deux niveaux de sécurité, c’est-à-dire au final masquer la sécurité d’arrière plan pour le consommateur de la façade.
Azure API Management permet de gérer l’authentification d’arrière plan grâce aux policies.
NOTE : Pour simplifier, les policies sont une fonctionnalité de scripting qui permet de personnaliser et étendre les traitements apportés par APIM sur les requêtes et les réponses.
Plus d’infos générale sur les policies ici et sur la syntaxe des expressions dans les policies ici.
Les policies de sécurité aujourd’hui disponibles sont limitées à :
Nous allons voir ici comment traiter un troisième type : l’authentification par Shared Access Signature.
Pour quoi faire : Concrètement et dans le contexte APIM, exposer une Queue ou Topic Azure Service Bus en tant que service tout en abstrayant la sécurité.
Principe : Une signature SAS permet de déléguer l’accès à des ressources Azure sans pour autant avoir à partager les clés de sécurité primaire ou secondaire du compte lui-même. Elle repose sur 2 éléments :
Les infos détaillées se trouvent ici.
Pour créer notre policy, il faut tout d’abord se connecter au Publisher portal de l’instance APIM (NOTE : à l’heure de ce post, Microsoft est en train de migrer une à une les fonctionnalités du Publisher et Developer portals dans le portail Azure standard).
Autant faire les choses proprement dès le début : au lieu de coder en dure les différents paramètres de l’authentification SAS, nous allons dès maintenant créer des propriétés qui contiendront ces paramètres, ce qui nous permettra d’isoler la logique d’authentification (la policy elle-même) et la configuration (les properties).
Elles sont maintenant accessibles en preview dans le portail Azure dans la section Names Values (mais toujours accessibles dans le publisher portal dans la section idoine Properties) :
Pour ajouter une property, il suffit de cliquer sur Add :
Puis de renseigner les infos suivantes (attention à la casse pour le nom de la propriété : par défaut le portail minimise la casse de ce qui est renseigné dans Display Name) :
Pour les 3 propriétés suivantes :
Les policies elles-mêmes se gèrent dans le Publisher portal dans la section Policies. On peut spécifier des policies à 4 niveaux en fonction de la granularité voulue :
Quel que soit le niveau de granularité retenu, toutes les customisations qui suivent sont à placer dans la section inbound des règles à appliquer.
La solution se compose de trois points :
1. Stocker les properties définies plus haut dans des variables :
La définition de variable se fait par l’utilisation de la policy set-variable :
<set-variable name= »resourceUri » value= »{{sbQueueURI}} » />
<set-variable name= »sasKeyName » value= »{{sbQueueKeyName}} » />
<set-variable name= »sasKey » value= »{{sbQueueKeyValue}} » />
2. Ajouter un header
L’ajout d’un header HTTP se fait grâce à la policy set-header. Le header voulu est Authorization.
Cette policy accepte un paramètre exists-action qui permet de déterminer le traitement à appliquer en cas de pré-existence du header dans la requête envoyée par le client. Dans notre cas le client n’est pas censé nous fournir ce header, il faut donc l’écraser :
<set-header name= »Authorization » exists-action= »override »>
<value></value>
</set-header>
3. Ajouter un script pour déterminer la valeur du header
La détermination du contenu du header se fait en ajouter une expression dans l’élément value de la policy set-header. Le script suivant implémente le mécanisme de création du jeton à partir des paramètres de l’authentification SAS.
La récupération des valeurs de variables définies ci-dessus se font grâce à l’instruction suivante :
string maVariable = (string) context.Variables.GetValueOrDefault(« <nom de la variable> »);
La policy complete devient :
<set-header name= »Authorization » exists-action= »override »>
<value>@{
string resourceUri = (string) context.Variables.GetValueOrDefault(« resourceUri »);
string sasKeyName = (string) context.Variables.GetValueOrDefault(« sasKeyName »);
string sasKey = (string) context.Variables.GetValueOrDefault(« sasKey »);
//set the token lifespan
System.TimeSpan sinceEpoch = System.DateTime.UtcNow.Subtract(new System.DateTime(1970, 1, 1));
var expiry = System.Convert.ToString((int)sinceEpoch.TotalSeconds + 60); //1 minute
string stringToSign = System.Uri.EscapeDataString(resourceUri) + « \n » + expiry;
System.Security.Cryptography.HMACSHA256 hmac = new System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes(sasKey));
var signature = System.Convert.ToBase64String(hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringToSign)));
//format the sas token
var sasToken = String.Format(« SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3} »,
System.Uri.EscapeDataString(resourceUri), System.Uri.EscapeDataString(signature), expiry, sasKeyName);
return sasToken;
}</value>
Une fois le tout sauvegardé, il suffit de faire un test avec un outil comme Postman. APIM devrait répondre avec un superbe:
Et la queue devrait contenir un nouveau message.
Ce mécanisme peut typiquement être utilisé pour recevoir des webhooks d’un système externe et les stocker dans une Queue, permettant ainsi de découpler la partie de messaging de la logique de traitement du hook.
Attention néanmoins à la sécurité de la façade! Les mécanismes de sécurité qu’il est possible de mettre en place dans ce scénario sont :