Authentification SAS dans Azure API Management

Simon Emmanuel RIVAS
Catégorie : Azure
18/07/2017

AUTHENTIFICATION D’ARRIÈRE PLAN DANS APIM

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 à :

  • l’authentification basique
  • l’authentification par certificat

Nous allons voir ici comment traiter un troisième type : l’authentification par Shared Access Signature.

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 :

  • Côté ressource Azure : Une règle d’autorisation partagée, paramétrée au niveau de la ressource Azure ciblée et matérialisée par :
    • Un nom de clé
    • Une clé (primaire et secondaire) : la représentation encryptée de la clé et des droits associés
  • Côté client : Un jeton (ou signature) SAS, qui est généré en encodant et cryptant une chaîne de caractères comprenant (entre autres) l’URI de la ressource, la clé SAS primaire ou secondaire et une durée de validité. Ce jeton est alors placé dans le hearder HTTP Signature

Les infos détaillées se trouvent ici.

CRÉATION D’UNE POLICY DANS APIM

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).

Création des properties

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 :

  • Clé : sbQueueKeyName
  • Valeur : sbQueueKeyValue. ATTENTION : pour celle-ci, il convient de cocher la case This is a secret, ce qui évitera à un curieux de récupérer la valeur de la clé depuis le portail.
  • URI : sbQueueURI. A savoir : Error! Hyperlink reference not valid.

Création de la policy

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 :

 

  • Instance : c’est le niveau le plus générique. Tout ce qui est spécifié ici est appliqué à l’ensemble des APIs et méthodes gérés dans l’instance APIM. Pour appliquer des règles à ce niveau, il suffit de ne rien sélectionner dans les zones produit, API et opération.
  • Produit : les API gérées dans l’instance peuvent être organisées sous forme de produit. Si un produit est sélectionné ici, alors les règles s’appliquent à toutes les API du produit
  • API : pour une API bien spécifique
  • Opération : pour une opération d’une API spécifique

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.

Scénario concret

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 :

  • Spécifier au niveau des produits auxquels l’API est rattachée qu’une souscription est requise (certains argumenteront que ce n’est pas un mécanisme de sécurité digne de ce nom, mais c’est toujours mieux que rien!)
  • Utiliser la policy ip-filter (Restrict caller IP) : permet d’accepter ou refuser une IP ou plage d’IP
  • Utiliser la policy rate-limit-by-key (Limit call rate by key): limite le rythme des appels en fonction d’une clé, qui est déterminable par expression (par ex : par adresse IP)
  • Tout autre mécanisme custom : par exemple la vérification d’un header HTTP contenant une signature du message. L’implémentation d’un tel mécanisme de vérification peut se faire avec une policy de contrôle choose / when / otherwise.