Accueil > OpenID Connect OAuth Server par DnC > Développer > Authentifier l’application

OpenID Connect (OIDC) offre un excellent moyen de sécuriser l’accès aux données protégées.

A condition toutefois de bien en comprendre les limitations et les risques en fonction de la configuration des parties (applications, serveur, ressources, réseau ...). Une mauvaise application des principes d’OIDC permettra à un malware d’accéder à des informations protégées !

Il ne s’agirait pas d’un défaut de sécurité d’OIDC, mais d’un défaut de mise en oeuvre d’OpenID Connect. En effet, toutes les configurations client-serveur ne présentent pas la même problématique de sécurité : le flux d’autorisation avec code (Authorization Code Grant) n’est pas une solution universelle à appliquer aveuglément.

La question est : comment authentifier l’application qui s’adresse à une ressource protégée ?

Nous présentons ici une analyse et les solutions offertes par OAuthSD.

Vérification de l’origine de la requête reçue par un serveur de ressource

  (publié initialement le dimanche 21 juillet 2019) par DnC

La transmission de jetons à des serveurs de ressource (RS) implique que ces derniers soient réceptifs à des jetons de toute provenance. Cela présente une opportunité pour un attaquant (une application étrangère) d’exploiter un jeton compromis ou volé.

Une fois de plus, l’analyse montre que nous ne sommes en mesure d’assurer pleinement la sécurité des données que dans le cas du flux "Authorization Code" pour des applications "avec back-end".

Considération sur la sécurité de la transmission des jetons aux serveurs de ressources

La proposition de standard RFC 6749 est bien claire sur la nécessité de n’utiliser les jetons d’accès que dans un espace de parties autorisées (§ 10.3.) :

Les identifiants de jeton d’accès (ainsi que tous les attributs de jeton d’accès confidentiels) DOIVENT être gardés confidentiels pendant le transit et le stockage, et partagés uniquement entre le serveur d’autorisation, les serveurs de ressources pour lesquels le jeton d’accès est valide et le client auquel le jeton d’accès est émis.

Si tous les serveurs de ressources (RS) auxquels sont transmis les jetons appartiennent à une même organisation (corporate realm), on peut supposer que les ressources sont situées dans un sous-réseau protégé par un pare-feu interdisant les requêtes depuis des applications extérieures au sous-réseau. C’est ce qui est désigné par "espace de confiance".
On peut alors :
- espérer que les jetons ne seront pas compromis, c’est à dire accessibles à des applications étrangères à l’organisation,
- supposer que toute application située dans ce sous-réseau et s’adressant aux ressources de ce même sous-réseau est autorisée à en recevoir les réponses.

C’est ce que l’on appelle des "applications de confiance". Mais notons que, du point de vue d’un serveur de ressource, la confiance ne résulte pas d’un caractère particulier de l’application que ce serveur pourrait constater par lui-même, mais du fait qu’elle se situe à l’intérieur d’un espace de confiance que l’on suppose inaccessible aux autres applications.

Cependant, même dans ce cas, une attaque bien élaborée pourrait conduire à voler le jeton pour l’exploiter avec une application étrangère.

De toutes façons, les suppositions ne sont jamais très bonnes en matière de sécurité [1].

Bien entendu, si on adresse un jeton à une ressource étrangère, il faut considérer le jeton comme compromis, car il pourra être réutilisé par une application étrangère.

La problématique est développée ici : Typologie des applications au regard de la sécurité des données.

Authentifier l’application cliente

Plutôt que se reposer sur la notion d’espace et d’applications de confiance, le serveur de ressource (Resource Server) doit pouvoir vérifier la légitimité du détenteur du token.

Mais comment ?

La spécification décrit clairement cette limitation (§10.3) :

Cette spécification ne fournit aucune méthode pour que le serveur de ressources s’assure qu’un jeton d’accès qui lui est présenté par un client donné a été émis pour ce client par le serveur d’autorisation.

 [2].

Dans le protocole HTTP, une ressource répondra à l’URL origine de la demande.

Deux types d’applications doivent être distinguées s’agissant de l’origine de la demande [3] :
- Les applications "avec back-end", c’est à dire émettant leurs requêtes depuis un serveur : la requête sera émise avec l’IP du serveur de l’application.
- Les applications "sans back-end", émettant leur requête à partir d’un user-agent (le navigateur de l’utilisateur final, le système d’exploitation de l’ordinateur hôte d’une application native etc.) : la requête sera émise avec l’IP de la connexion de l’user-agent, éventuellement masquée par un proxy.

Dans l’état actuel du développement d’OAuthSD, nous ne considérerons que les "applications avec back-end" dont l’IP du serveur hôte garantit l’identité [4]. Autrement dit, nous ne sommes en mesure d’assurer la sécurité des données que dans le cas du flux "Authorization Code" pour des applications "avec back-end".

OAuthSD offre deux moyens de vérifier la légitimité d’une application "avec back-end" présentant le jeton en se fondant sur l’IP de l’origine :
- par introspection avec l’IP du demandeur,
- avec les déclarations supplémentaires du jeton JWT ’cip_hash’ ou ’cip’.

Les IP de l’application cliente, auxquelles sera comparée l’IP du demandeur, sont déterminées de la façon suivante :
- si l’application cliente est enregistrée avec un champ ’ip’ de la table ’clients’ non vide, OAuthSD utilisera ces IP(s),
- sinon, les IP(s) seront obtenues en interrogeant le DNS avec le nom d’hôte de l’URL de retour (redirect URI) enregistrée pour l’application [5] [6].
OAuthSD permet de prendre en compte un éventuel proxy (de confiance) pour accéder à la véritable IP de l’application.

Vérification par introspection avec l’IP du demandeur

Le projet de RFC 7662 évoque le sujet de façon implicite. Il est en effet indiqué dans la section 2.1 :

"Le point de terminaison d’introspection PEUT accepter d’autres paramètres OPTIONNELS pour fournir un contexte supplémentaire à la requête. Par exemple, un serveur d’autorisation peut souhaiter connaître l’adresse IP du client accédant à la ressource protégée pour déterminer si le client présentant le jeton est le bon. La définition de ce paramètre ou de tout autre paramètre sort du cadre de la présente spécification, ... "

OAuthSD met ceci en œuvre de la manière suivante :

Si le RS appelant le point de terminaison Introspection transmet l’adresse IP de son propre demandeur au moyen du paramètre ’requester_ip’, l’introspection vérifie que cette adresse IP se trouve dans le même sous-réseau que l’application cliente identifiée par la déclaration ’aud’.

Afin d’éviter de répondre à un malware une ressource protégée recevant un jeton d’identité doit le valider par introspection en transmettant le paramètre ’requester_ip’.

Sur OAuthSD, pour que cette vérification soit effective, il convient de :
- renseigner l’IP des applications clientes lors de leur enregistrement (à défaut l’IP du domaine de l’URI de retour sera utilisé),
- de régler à ’true’ la constante de configuration CHECK_CLIENT_IP.

Notes :
- D’une façon générale, l’IP des applications faisant appel aux contrôleurs Introspect ou UserInfo est vérifiée comme indiqué ci-dessus, sans qu’il soit nécessaire de fournir le paramètre ’requester_ip’, afin de vérifier qu’il s’agit bien de l’application cliente enregistrée.
On comprendra que, dans le cas d’une demande effectuée à un serveur de ressource qui fait appel au contrôleur Introspect, il est nécessaire que le serveur de ressource fournisse l’IP du demandeur, sinon ce serait l’IP du serveur de ressource qui serait contrôlée, et non celle du demandeur.
- Notons qu’il existe également une vérification optionnelle de l’appartenance à un même domaine ou sous-domaine (voir la constante de configuration CHECK_SAME_DOMAINS) effectuée par le contrôleur Introspect.

Exemples d’appel à l’introspection avec le paramètre ’requester_ip’

Par en-tête :

PHP

  1. // Method Bearer + parameters by Post
  2. $data1 = array(
  3.     'requester_ip' => $_SERVER['SERVER_ADDR'],  
  4. );
  5.  
  6. $h = curl_init($introspection_endpoint);
  7. curl_setopt($h, CURLOPT_HTTPHEADER, array(
  8.     'Content-Type: application/x-www-form-urlencoded',
  9.     'Authorization: Bearer ' . $res1['id_token']
  10. ));
  11. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  12. curl_setopt($h, CURLOPT_POST, 1);  
  13. curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data1));
  14.  
  15. $res = curl_exec($h);
  16.  
  17. ...

Télécharger

Par Post :
PHP

  1. // Post Methode  
  2. $data1 = array(
  3. 'token' => $res1['id_token'],
  4. 'requester_ip' => $_SERVER['SERVER_ADDR'],  
  5. );
  6.  
  7. $trace = $_SESSION['trace'];
  8. $trace .= '----- Step 3 : JWT Introspection -----' . "<br />";
  9. $trace .= 'access token : ' . $access_token . "<br />";
  10. $trace .= 'data : ' . print_r($data1,true) . "<br /><br />";
  11. $_SESSION['trace'] = $trace;    
  12.  
  13. $h = curl_init($introspection_endpoint);
  14. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  15. curl_setopt($h, CURLOPT_TIMEOUT, 100);    //100 : DEBUG
  16. curl_setopt($h, CURLOPT_POST, true);
  17. curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  18. curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));  
  19. curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data1));
  20.  
  21. $res = curl_exec($h);
  22.  
  23. ...

Télécharger

Voyez également :
- API Open ID Connect : Introspection,
- Plus loin avec l’Introspection,
- Envoi d’une requête à un serveur de ressource (RS).

Vérification avec les déclarations supplémentaires du jeton d’identité ’cip_hash’ ou ’cip’

OAuthSD introduit dans la charge utile du jeton JWT une déclaration ’cip’ ou ’cip_hash’ pour permettre la vérification de l’IP du demandeur [7] [8].

Cette vérification pourra être effectuée localement, ou par l’introspection, appelée comme ci-dessus avec le paramètre ’requester_ip’.

’cip_hash’
Dans le cas général, une application n’a qu’une IP. Dans ce cas, c’est cette déclaration qui sera générée, et non ’cip’, avec l’avantage de masquer l’IP de l’application cliente en la transmettant sous la forme d’un hash. Si l’application a plusieurs IP, et si la constante de configuration FORCE_CIP_HASH est true, la première IP de la liste sera utilisée pour générer la déclaration.

’cip’
La déclaration ’cip’ est générée lorsque les IP de l’application clientes sont multiples. Sa valeur est une suite d’adresses IPv4 séparées par un espace.
Cependant, si la constante de configuration FORCE_CIP_HASH est true, la déclaration ’cip_hash’ sera toujours émise à la place.

Notes :
- L’une ou l’autre de ces déclarations supplémentaires sera toujours ajoutée à la charge utile du jeton d’identité JWT du flux OpenID Connect Authorization Code, mais pas dans son pendant OAuth 2.0.
- Cette méthode a l’avantage de permettre la vérification par la ressource protégée (rappelons que la charge utile du jeton JWT est simplement encodée URL64). Ceci est utile pour bloquer une attaque sans avoir à faire appel à l’introspection, ou quand la validation du jeton JWT est effectuée localement sans passer par l’introspection.
- Le contrôleur Introspect effectuera également la vérification de l’IP avec ces déclarations. Dans le cas d’un serveur de ressource, il est nécessaire que ce dernier transmettre le paramètre ’requester_ip’.
- Un proxy peut se situer entre l’application cliente et le serveur d’authentification. Les paquets adressés par le proxy portent l’IP du proxy, et non l’IP de l’hôte de l’application cliente. Dans ce cas, la vérification par l’IP va échouer, sauf dans le cas d’un "proxy de confiance" traité ci-après.

Autre méthode : avec la déclaration audience du jeton JWT

La bibliothèque oauth2-server-php réduit "aud" à l’identifiant du client client_id.

Pourtant la spécification prévoit :

"Il PEUT aussi contenir des identifiants pour d’autres audiences. Dans le cas général, la valeur aud est un tableau de chaînes sensibles à la casse".

Certaines implémentation d’OpenID Connect considèrent que la déclaration audience relève du niveau applicatif, ce qui signifie que sa valeur est une convention établie au sein d’une même organisation (corporate realm) entre l’application et le serveur de ressource.

Dans cette optique, la déclaration "aud" est un tableau qui peut contenir les IPs des applications autorisées à présenter le jeton. Ceci permettra au serveur de ressource de vérifier directement l’IP et ainsi bloquer les malwares sans avoir à émettre une demande d’introspection.

La déclaration audience étant définie au niveau de la bibliothèque oauth2-server-php, nous devons modifier celle-ci pour intégrer l’IP de l’application cliente (fournie lors de l’inscription de l’application sur le serveur) dans la déclaration "aud". Ce développement est en cours.

Prise en compte d’un "proxy de confiance"

La question est de savoir si on peut faire confiance à la déclaration HTTP_X_FORWARDED_FOR par laquelle un proxy retransmet l’IP de la requête d’origine.

Un éventuel proxy situé entre le réseau Internet et le serveur d’authentification est maîtrisé par l’organisation et peut être considéré comme étant de confiance.

OAuthSD permet de désigner ces proxys par leur IP dans la constante de configuration TRUSTED_PROXIES. Si la constante de configuration USE_PROXY est true, OAuthSD remplacera ces IP par celle fournie par la déclaration HTTP_X_FORWARDED_FOR.

Notes :
- Parmi les proxies de confiance on peut trouver les proxies inverses (reverse proxy) dont la fonction est de fournir un système de cache. Le principe même de l’authentification exclut l’usage de cache.
- Parmi les proxies de confiance se trouvent également les équilibreurs de charge (load balancer). Certains de ces proxies n’ont pas une IP fixe. Dans ce cas, il sera impossible d’appliquer la vérification du client par IP.

SAFEIP : Élimination des "proxies privés"

On notera que cette notion de proxy de confiance est l’inverse de ce qu’un grand nombre d’internautes désignent sous le vocable de proxy dont ils attendent qu’ils masquent leur adresse IP. De tels proxies se disent "anonymes" et sont désignés sous le vocable "proxy privé" ou "private proxy", ce qui n’a rien à voir avec le fait qu’ils soient privés, mais qu’ils ont pour but d’assurer l’anonymat de l’internaute.

OAuthSD comporte une fonctionnalité "SAFEIP" permettant de bloquer les adresses IP douteuses, parmi lesquelles se trouvent la plupart de celles de tels proxies.

OAuthSD offre ainsi deux mécanismes complémentaires : La prise en compte des proxies de confiance permet d’autoriser les proxies de l’interface réseau local / Web, tandis que la fonction "SAFEIP" permettra de bloquer les proxies douteux du Web.

Notes

[1C’est un euphémisme pour ne pas dire que nous sommes en présence d’une faille de sécurité béante qui n’est pas assez prise en considération.

[2Peut-on trouver des solutions dans d’autres documents ? Hélas, non ! On peut lire dans le document OAuth 2.0 Token Exchange :

Bien que quelques nouvelles déclarations JWT soient définies pour permettre l’expression de la sémantique de la délégation, la syntaxe, la sémantique et les caractéristiques de sécurité spécifiques des jetons eux-mêmes (à la fois ceux présentés au serveur d’autorisation et ceux obtenus par le client) sont explicitement hors de portée et aucune exigence n’est formulée sur le modèle de confiance dans lequel une implémentation peut être déployée.
Des profils supplémentaires peuvent fournir des exigences plus détaillées concernant la nature spécifique des parties et la confiance impliquées, par exemple si la signature et / ou le cryptage des jetons est nécessaire ou si des jetons de type preuve de possession seront nécessaires ou émis ; Cependant, ces détails seront souvent des décisions de politique prises en fonction des besoins spécifiques de déploiements individuels et seront configurés ou mis en œuvre en conséquence.
.

[3On classe souvent une application "avec backend" comme "application confidentielle" ou "privée" et une application "sans back-end" comme "application publique". C’est une confusion, voir : Typologie des applications au regard de la sécurité des données.

[4On peut parler "d’IP de l’application" mais notons que plusieurs applications pourraient partager une même IP.

[5Il convient donc de s’assurer de l’intégrité des DNS. Le mieux serait que les DNS des applications et le DNS auquel accède le serveur (un DNS local de préférence) se trouvent dans un espace de confiance, et/ou qu’ils soient protégés par DNSSEC.

[6Cette méthode peut introduire un délai de réponse de la ressource indésirable, raison pour laquelle OAuthSD propose l’enregistrement de l’IP.

[7Nous vantons l’intérêt du jeton d’Identité au format JWT signé pour lier l’identité de l’utilisateur final, celle de l’application et les portées d’autorisation. Cependant, un serveur de ressource HTTP ne peut identifier l’origine de la requête que par son IP dans la relation serveur-serveur. L’identifiant du client transmis par le jeton n’est pas probant puisque le jeton peut avoir été "volé". Il est donc indispensable d’incorporer au jeton d’identité l’IP de l’application légitime afin qu’un serveur de ressource puisse la vérifier.

[8Peut-on utiliser la déclaration ’aud’ pour transmettre cette URL ? Cela semble le cas si l’on s’en tient à la spécification du JWT pour laquelle ’aud’ peut être une liste d’URI. Mais la spécification OpenID Connect est plus restrictive :

aud
OBLIGATOIRE. Audience (s) pour laquelle ce jeton d’identifiant est destiné. Il DOIT contenir le client_id OAuth 2.0 de la partie de confiance en tant que valeur d’audience. Il PEUT aussi contenir des identifiants pour d’autres audiences. Dans le cas général, la valeur aud est un tableau de chaînes sensibles à la casse. Dans le cas spécial usuel où il y a un public, la valeur aud PEUT être une chaîne sensible à la casse.

En conséquence, la bibliothèque oauth2-server-php fixe la valeur du paramètre ’aud’ à l’identifiant de l’application cliente. La déclaration ’aud’ n’est pas le bon choix pour permettre à OAuthSD de transmettre l’IP du client.

Typologie des applications au regard de la sécurité des données

  publié le par DnC

Application "Web", "publique", "privée", "confidentielle", "avec back-end", "sans-backend", "native", "hybride", "à page unique" (SPA) etc. Comment s’y retrouver ? Quelle est leur sécurité selon les flux d’autorisation mis en oeuvre ?

Avertissement : Cet article est encore en cours de rédaction. Cependant, nos échanges récents avec des développeurs ont montré qu’il importait de bien comprendre la configuration des applications par rapport à l’authentification afin de mettre en lumière les limitations d’OIDC et les risques pour la sécurité des données.


Application de confiance, espace de confiance

Un des dangers d’une compréhension superficielle d’OpenID Connect réside dans le fait qu’il est impossible dans certaines configurations d’authentifier l’application. La sécurité des données ( à quelle application sont-elles transmises ? ), repose alors sur la notion d’espace de confiance.

La proposition de standard RFC 6749 est bien claire sur la nécessité de n’utiliser les jetons d’accès que dans un espace de parties autorisées (§ 10.3.) [1] :

Les identifiants de jeton d’accès (ainsi que tous les attributs de jeton d’accès confidentiels) DOIVENT être gardés confidentiels pendant le transit et le stockage, et partagés uniquement entre le serveur d’autorisation, les serveurs de ressources pour lesquels le jeton d’accès est valide et le client auquel le jeton d’accès est émis.

Voyez ici une approche pratique du sujet : Vérification de l’origine de la requête reçue par un serveur de ressource.

Avant de choisir un flux OpenID Connect, il est important d’identifier la configuration des parties prenantes.

Il faut tout d’abord distinguer la Programmation côté serveur de la programmation côté client, qui produisent respectivement des applications avec ou sans back-end.

Programmation côté serveur (server-side programming)

Voir : MDN : Qu’est-ce que la programmation côté serveur ?.

Les serveurs web attendent des requêtes du client, les traitent quand elles arrivent, et répondent au navigateur web avec une réponse HTTP. La réponse contient un statut qui indique si la requête a pu être traitée avec succès ou non (exemple "HTTP/1.1 200 OK" pour indiquer le succès).

Le corps de la réponse, si la requête réussit, contient alors la ressource demandée (comme une page HTML, une image, etc...), que le navigateur web peut alors afficher.

Site statique
Un site statique est un site qui renvoie du contenu codé en dur, c’est à dire le contenu d’un fichier, quand une ressource donnée est demandée.

Site dynamique
Un site dynamique est un site dont une partie des réponses sont générées dynamiquement, au moment de la requête. Sur les sites dynamiques, les pages HTML sont normalement créées en insérant des données d’une base de données dans des espaces réservés à l’intérieur de templates HTML (c’est une manière beaucoup plus efficace que des fichiers statiques pour stocker de grandes quantités de contenu).

Un site web dynamique peut retourner des données différentes pour une URL, en se basant sur les informations fournies par l’utilisateur final (c’est ici que nous rejoignons notre sujet de l’authentification avec OpenID Connect) ou ses préférences stockées, et peut effectuer des opérations avant de retourner la réponse.

La plupart du code pour maintenir un site web dynamique doit s’exécuter sur le serveur. La création de ce code est ce qu’on appelle la "programmation côté serveur" ou "codage back-end".

Les applications construites sur un tel serveur sont dénommées "application avec back-end".

Puisque notre interrogation porte sur la sécurité de l’authentification, cette citation de MDN nous donne la réponse définitive à la question :

Accès contrôlé au contenu. La programmation côté serveur permet aux sites de restreindre l’accès aux utilisateurs autorisés et de ne servir que les informations qu’un utilisateur a la permission de voir.

Il semble que l’on puisse admettre que seule la programmation côté serveur permette cela.

Mais cela suffit-il à catégoriser les applications par rapport à la possibilité de les authentifier ?

Application "avec" ou "sans back end"

Dans un environnement client-serveur, on considère que le client ou agent utilisateur (user-agent), généralement le navigateur, fait office de front-end alors que le serveur fait office de back end.

Application "avec back end"

Une application "avec back end" fait appel pour l’essentiel de son fonctionnement à un logiciel tournant sur un serveur et produisant des sorties vers l’agent utilisateur.
La forme la plus courante consiste, côté front-end, à limiter l’utilisation du Javascript à l’appel des données (en Ajax) et à leur présentation. Une application avec back-end représente un cas de programmation côté serveur (server-side programming).

Les applications "avec back end" permettent de faire circuler les jetons et les données sensibles sur des liaisons serveur-serveur protégées par TLS et hors de portée de l’agent utilisateur (généralement le navigateur de l’utilisateur final) ou des applications étrangères. Elles répondent à une URL fixe.

Le flux "Authorization Code" permet de s’assurer de l’identité d’une application cliente au moyen de l’URL de rappel (Callback URL ou redirection endpoint URI), enregistrée sur l’OP avec l’application.

Application "sans back_end"

Les copies d’une application "sans back end" sont installées et fonctionnent de façon autonome sur les agents des utilisateur finaux (généralement leur navigateur).

L’URL de rappel pointe vers l’agent utilisateur, et non vers l’application d’origine. Le flux d’Autorisation avec Code peut être inopérant car on ne peut pas s’assurer que le code qui est exécuté est bien le code original, ce qui ouvre la porte à des applications malveillantes situées sur cet agent. Dans l’état actuel, il faut considérer qu’il n’est pas possible d’authentifier une application sans back-end.

Notons que l’on confie à l’utilisateur le soin de reconnaître l’application comme étant la bonne, ce qui explique que la problématique de l’hameçonnage (phishing) soit au cœur de la sécurité des applications mobiles.

Application "confidentielle"

Une application est dite "confidentielle" si le public ne peut accéder au secret de l’application et aux jetons transmis.

On notera que le caractère confidentiel (ou privé) d’une application ne lui est pas inhérent, mais qu’il résulte d’une configuration permettant d’interdire l’accès au secret de l’application et aux jetons, mais aussi de la mise en œuvre de toutes les bonnes pratiques de sécurité aboutissant sur une protection effective.

On considère que les identifiants d’une application sans back-end, qui doivent donc figurer dans le code, sont accessibles, de même que les jetons, par rétro-ingénierie ou par des applications tierces exécutées dans le même environnement. Une application "sans back-end" serait donc "non confidentielle", mais cela est modulé par la notion d’espace de confiance.

Application publique (Public client app)

Par opposition aux applications confidentielles, une application publique est une application dont le code est accessible par des étrangers à l’organisation gérant le serveur d’authentification. Elle est par nature non confidentielle, non de confiance car situé en dehors de l’espace de confiance.
Il semble que, pour les RFC, la notion d’application publique implique que l’application n’a pas de secret (ou ne devrait pas en avoir, en tous cas le secret d’une application publique ne sert à rien) .

IP d’une application avec ou sans backend

Lorsqu’une application émet une demande vers un serveur de ressource (RS) :
- les applications "avec back end", émettent leurs requêtes depuis un serveur : la requête sera émise avec l’IP du serveur de l’application.
- les applications "sans back end", émettent leur requête à partir d’un user-agent (le navigateur de l’utilisateur final, le système d’exploitation de l’ordinateur hôte d’une application native etc.) : la requête sera émise avec l’IP de la connexion de l’user-agent, éventuellement masquée par un proxy. Donc rien de prévisible.

Dans le cas d’une application avec backend, l’IP permet à une ressource d’identifier l’application, ce qui n’est pas possible avec une application sans back-end.

Application "Web"

Une application "Web" est ce à quoi nous avons d’abord été habitués : un navigateur standard permet de naviguer dans les pages de l’application en s’appuyant sur un serveur qui peut fournir la totalité de la page, ou y inscrire du code Javascript qui effectuera au sein du navigateur une partie du travail nécessaire, qu’il s’agisse de présentation ou de calculs "métier". Ce qui caractérise l’application Web, c’est sa dépendance au serveur, exigeant une connexion permanente pour fonctionner : une page ne peut être élaborée et présentée s’il n’y a pas connexion du navigateur au serveur.

Une application Web est donc une "application avec back-end". La totale dépendance au serveur fait des applications Web le domaine idéal d’application du flux "Authorization Code". C’est une configuration facilitant la protection des données dans un espace privé. De plus, les données circulent sur le Web protégées par TLS. Elles ne sont pas accessibles au niveau du système d’exploitation par les autres applications fonctionnant à côté de l’agent utilisateur.
Une application Web est donc potentiellement confidentielle.

Enfin, seul le code situé sur le serveur (et non le code Javascript téléchargée par les pages) est digne de confiance.

Il faut garder à l’esprit que certaines vulnérabilité existent (dont l’injection de code, les attaque du type Man-In-The-Middle etc.), notamment lorsque des d’applications Web étrangères sont accédées de façon simultanée avec le même user-agent, ou à partir du même poste de travail, ou à partir du même réseau local. OIDC n’apporte dans ce domaine aucune protection qui se substituerait aux bonnes pratiques de sécurité.

Applications "native", et "hybride"

Le qualificatif "native" est apparu pour désigner une application pour smartphone ou tablette développée spécifiquement pour un système d’exploitation mobile ( iOS ou Android ). Il serait plus exact de dire qu’une application "native" s’exécute sur le système d’exploitation d’un ordinateur hôte ( non nécessairement mobile, aussi bien une application lancée sur le système d’exploitation d’un ordinateur de bureau [2] ) mais qui ne nécessite pas de connexion permanente à internet contrairement à une application Web.

Une application "hybride" est une application enveloppée dans un conteneur natif ayant la capacité de charger du code HTML depuis un serveur et de l’interpréter. L’application est généralement formée de pages contenant du code JavaScript qui charge et élabore les données nécessaires pour un affichage en HTML5. Dans certains cas, le contenu est totalement élaboré côté serveur.

Une particularité très importante est la capacité des applications "natives" à accéder aux ressources du système d’exploitation sous-jacent, (y compris le système de fichier, les bases de données etc.) ainsi qu’à la communication inter-applications, localement ou par le réseau. Par contraste, l’accès aux ressources du système est interdit aux applications Web et leur communication est contrainte par la politique "même domaine" (same domain policy).

Pour ce qui est de la sécurisation de l’accès aux données avec OAuth, on retiendra les observations suivantes :
- Une application native peut être considérée comme étant "avec back-end" s’il revient toujours au serveur d’accéder aux serveurs de ressource (RS). On comprend que ce sera souvent le cas d’une application hybride.
- Une application native est une instance d’une application installée sur une machine particulière. Son code est donc accessible, offrant la possibilité de découvrir le secret de l’application.
- Les données circulent au niveau du système d’exploitation sans chiffrement TLS et sont donc exposées à une interception. Un appareil mobile, du fait de son usage très varié, offre un contexte très risqué du fait de nombreuses applications plus ou moins sûres.
- En revanche, une application native est à l’abri d’un éventuel malware exécuté sur un agent utilisateur local.

La distinction "native" - "hybride" n’est pas très significative : la différence réside dans leur plus ou moins grande capacité à élaborer localement les données "métier" et leur présentation. On omet le plus souvent le qualificatif "hybride" pour inclure ces applications sous le seul qualificatif de "native". Il faudra cependant retenir la première des observations ci-dessus en faveur de l’application "hybride", car la relation avec un back-end permet l’authentification avec le flux Authorization Code qui offre la meilleure sécurité.

Application cliente publique native (public native app clients)

Ce terme désigne les applications natives qui ne sont pas situées dans l’espace de confiance d’une organisation dans lequel se situe également le serveur d’authentification.

Une application "avec back-end" est-elle nécessairement "confidentielle" ?

On classe souvent une application "avec backend" comme application confidentielle ou "privée" et une application "sans back-end" comme application publique.

L’équivalence "avec-backend" <=> "confidentielle" résulte de la capacité à protéger les identifiants de l’application et les jetons. En effet, une application "avec backend" peut être gérée de façon privée (sur un serveur privé d’accès réservé) ce qui interdit au public d’accéder au code et donc aux identifiants.

Cependant, il est nécessaire d’utiliser le flux "Authorization Code" pour que le secret de l’application et les jetons circulent de serveur à serveur et puissent être considérés comme non divulgués.

Une application "sans back-end" est-elle nécessairement non confidentielle ?

Le terme "sans back-end" qualifie une application téléchargée et installée par un utilisateur sur son navigateur ou comme "application native" fonctionnant sur le système d’exploitation d’un mobile ou d’une machine de bureau.

Dans un cas comme dans l’autre, on considère que les identifiants de l’application (qui figurent dans le code) sont accessibles, de même que les jetons, par rétro-ingénierie ou par des applications tierces exécutées dans le même environnement. Une application "sans back-end" serait donc "non confidentielle".

La méthode PKCE (Proof Key for Code Exchange) ou "clé de vérification pour l’échange de code" permet à une application d’éviter d’incorporer dans son code le secret de l’application et de le transmettre. Voir : Clé de vérification pour l’échange de code (PKCE). Beaucoup se l’accordent à dire : ce n’est pas pour autant un apport à la sécurité.

Ceci permet d’éviter de propager un secret, mais n’apporte aucune information nouvelle sur l’identité de l’application puisque c’est une instance publique qui est ainsi identifiée. La conformité au modèle original n’est pas garantie.

On pourrait aussi considérer que la sécurité d’une application sans back-end est acquise dans un espace de confiance. On observera alors que la sécurité dépend de l’environnement, non de la nature de l’application.

Application à page unique (Single Page Application, SPA)

Une application à page unique (SPA) est une application Web qui charge une seule page HTML à partir du serveur Web, puis la met à jour de manière dynamique en réponse à l’interaction de l’utilisateur et à d’autres événements.

Plusieurs architectures permettent de créer des applications à page unique. La tendance est l’architecture "serveur léger", qui fournit la page initiale contenant du code Javascript qui assurera la logique et les transitions d’état de l’application, celle-ci obtenant ses données d’API RESTful protégées par OAuth 2.0.

Une application à page unique, bien qu’étant issue d’un serveur, doit être considérée comme étant publique, et non comme une application avec back-end, car tout son code se trouve transféré sur l’user-agent. Pour en savoir plus : OIDC et les Application à page unique : exemple d’une belle mascarade !.

Quelques autres considérations à propos de l’authentification des applications

Des systèmes tels que Active Directory (Kerberos) identifient l’utilisateur et la machine hôte au niveau de la couche de communication. Une fois l’authentification effectuée, toutes les applications exécutées sur la machine du client sont mises sur le même plan, bonnes ou malveillantes. La notion de service (un logiciel installé sur une machine et un port) permet d’ authentifier une application avec back-end. L’authentification des applications sans back-end n’est pas envisageable.

L’authentification OpenID Connect concerne la couche applicative. Dans le cas des applications avec back-end, nous avons vu que l’authentification de l’application était effective. Mais dans le cas d’applications mobiles ( sans back-end ), nous sommes devant une difficulté. En effet, la notion d’application "publiques" ( sans secret ) interdirait l’authentification de l’application, ouvrant la porte aux applications malveillantes. Mais le fait de se situer sur la couche applicative nous laisse des solutions, au moins dans certaines configurations.

Notons que les méthodes de "fingerprinting" identifient l’user-agent et la machine hôte, pas l’application. En fait, l’user-agent est une application qui fait obstacle à l’identification de l’application réelle par le serveur d’authentification. La direction de recherche serait donc celle du ’footprinting’ qui permettrait d’identifier l’application comme étant bien celle qui a fait l’objet de l’authentification initiale, donc celle qui est légitimement associée au jeton JWT qu’elle présente aux ressources protégées.

Une conclusion s’impose

l’analyse montre que nous ne sommes en mesure d’assurer pleinement la sécurité des données que dans le cas du flux "Authorization Code" pour des applications "avec back-end".

Notes

[1L’attention du lecteur est appelée sur le risque qu’il y a à lire le paragraphe qui suit au bord d’un gouffre, en traversant la rue ou, plus généralement, dans toute situation où un vertige pourrait avoir des conséquences fatales.

[2L’usage commun à de nombreux sites Web consiste à réduire la notion de ’native" aux applications de mobile développées sous iOs ou Android. La RFC 8252 va dans notre sens dans le paragraphe 7.3 en incluant dans les applications natives "celles des systèmes d’exploitation de bureau".

OIDC et les Application à page unique : exemple d’une belle mascarade ! Incompétence ou mensonge délibéré ?

  publié le par DnC

Une application à page unique (SPA) est une application Web qui charge une seule page HTML à partir du serveur Web, puis la met à jour de manière dynamique en réponse à l’interaction de l’utilisateur et à d’autres événements.

Tirant argument des progrès des navigateurs, permettant à Javascript de mettre en œuvre les règles CORS et d’accéder au cryptage, certains préconisent de mettre en œuvre le flux de code avec autorisation plutôt que le flux implicite.

La magie du flux "Authorization Code" est alors évoquée pour prétendre que l’accès aux données est sécurisé. Ce n’est que pure mascarade ! Bling bling bling ...

Application à page unique

Plusieurs architectures permettent de créer des applications à page unique. La tendance est l’architecture "serveur léger", qui installe sur l’agent utilisateur ( en général le navigateur de l’utilisateur ) la page initiale contenant du code Javascript. Ce code assurera la logique et les transitions d’état de l’application, celle-ci obtenant ses données d’API RESTful protégées par OAuth 2.0. Dans cette configuration, l’application réside sur l’agent utilisateur et non sur le serveur.

Les SPA peuvent (maintenant ?) mettre en œuvre le flux de code avec autorisation (Authorization Code Grant)

Il est de nombreuses configurations dans lesquelles les hôtes de l’application et les points de terminaison des services se situent sur des serveurs différents : serveur d’authentification, ressources protégées (RS) ou API.

Les applications à page unique en sont un bon exemple.

En utilisation Web normale, cette approche multi-hôte ou, plus précisément, "multi-origines" est restreinte pour de bonnes raisons de sécurité. Les déclarations de partage de ressources multi-origines ( Cross-Origin Resource Sharing, CORS ) permettent de contourner la contrainte.

Les navigateurs se sont améliorés depuis la conception du flux implicite. La mise en œuvre de CORS pour autoriser la demande de jeton à partir de JavaScript est maintenant disponible, ainsi que les API de cryptographie nécessaires pour valider la signature du jeton JWT.
Il est donc techniquement possible d’utiliser le flux de code d’autorisation pour l’authentification et l’autorisation des applications sans back-end, y compris pour les applications à page unique.

Faut-il en déduire pour autant que l’utilisation de ce flux, dans cette configuration, garantit la sécurité des données ?

Avec les SPA, les problèmes de la sécurité de l’authentification restent posés

Les SPA sont des applications "sans-back-end". Leur code se trouve sur l’agent utilisateur, donc n’importe où, contrairement aux applications "avec back-end" dont le code est protégé au sein d’un serveur. A ce titre :

- Sont exposés dans le code, de façon plus ou moins accessible au public :
- l’identifiant et le secret de l’application,
- les jetons d’accès ou d’identité.

Avec l’identifiant et le secret, une application malicieuse peut se procurer des jetons. Avec les jetons, elle pourrait demander des données au nom de la véritable application.

- L’URL de redirection est celle de l’application, donc celle de son hôte, c’est à dire l’user-agent. Elle ne peut être inscrite sur le serveur et doit être fournie par l’application.
Si on est dans le cas d’user-agent de stations de travail ( desktops ) situés dans un espace de confiance ( un ou plusieurs domaines connus ), il reste encore possible de comparer cette URL à un modèle enregistré sur le serveur d’autorisation. Mais même dans ce cas, si l’application malveillante se trouve sur le même hôte [1], la distinction est impossible. Si on a affaire à une application de mobile, cela devient vraiment impossible.

- Dans le cas d’une application de mobile, la demande de jeton au point d’entrée Token et la réponse ne circulent pas dans une liaison de serveur ( celui de l’application ) à serveur ( le serveur d’authentification ) dans laquelle les deux extrémités sont identifiées, mais entre différents mobiles non identifiés et le serveur d’authentification. Une des qualités reconnues du flux de code d’autorisation appliqué aux clients avec back-end disparaît.

Dans le cas d’une SPA comme dans celui de toute application sans back-end [2], il n’est pas possible d’identifier avec certitude l’application présentant le jeton comme étant bien celle qui l’a obtenu au cours de la procédure d’authentification.

Encapsuler les données cryptées dans une classe Javascript ???

Avec les SPA, il est possible d’éviter de stocker des informations d’identification selon les méthodes classiques, telles que les jetons d’accès dans les cookies et le stockage de session. Au lieu de cela, on conservera les données sensibles dans des variables JavaScript.

Certains recommandent alors d’utiliser une classe Javascript pour stocker et fournir ces variables. Cette méthode reçoit le joli nom d’"encapsulation" qui donne un aspect sérieux et innovant à une méthode pourtant bien banale.

Cependant, il ne s’agit là que d’une méthode d’obfuscation dont l’efficacité est insuffisante face à un attaquant déterminé. Un simple debugger permet d’exécuter pas à pas le code et de voir passer les variables.

Pourquoi les spécifications confirment-elles une pratique douteuse ?

On peut lire dans le document Health Relationship Trust Profile for OAuth 2.0 :

2.1.3. Client intégré au navigateur avec délégation d’utilisateur

Ce type de client s’applique aux clients qui agissent pour le compte d’un propriétaire de ressource particulier et nécessitent la délégation des droits de cet utilisateur pour accéder à la ressource protégée. De plus, ces clients sont intégrés à un navigateur Web et partagent efficacement une session active entre les systèmes.

Ces clients utilisent le flux implicite de OAuth 2 en envoyant un propriétaire de ressource au noeud final d’autorisation pour obtenir une autorisation. L’utilisateur DOIT s’authentifier auprès du noeud final d’autorisation. Le navigateur Web de l’utilisateur est ensuite redirigé vers un URI hébergé par le client, à partir duquel le client peut directement obtenir un jeton d’accès. Étant donné que le client lui-même ne s’authentifie jamais auprès du serveur et que le jeton est mis directement à la disposition du navigateur, ce flux ne convient que pour les clients incorporés dans un navigateur Web, tel qu’un client JavaScript sans composant de serveur principal. Dans la mesure du possible, il est préférable d’utiliser le flux de code d’autorisation en raison de ses propriétés de sécurité supérieures.

Ce type de client NE DOIT PAS demander ou recevoir un jeton d’actualisation. Les jetons d’accès émis à ce type de client DOIVENT être de courte durée et DEVRAIENT être rejetés à l’expiration de la session authentifiée de l’utilisateur avec le client.

Il est donc bien reconnu qu’il y a un problème que le flux de code d’autorisation ne résoudra pas. Ce flux est sans doute "de sécurité supérieure", mais dans le cas des applications sans back-end il échoue à identifier l’application.

Pourtant, la rédaction du paragraphe donne à penser que le flux de code d’autorisation fera mieux que le flux implicite. Et pas un mot n’est dit sur l’exposition du secret de l’application.

C’est une mascarade : n.f. comédie hypocrite, mise en scène trompeuse.

Un article présentant le contre et optant pour le pour

Voici un article intéressant pour l’analyse qui est faite : SECURELY USING THE OIDC AUTHORIZATION CODE FLOW AND A PUBLIC CLIENT WITH SINGLE PAGE APPLICATIONS.

L’auteur préconise l’utilisation du flux d’autorisation avec code pour les SPA publiques, mais en fait une analyse détaillée qui ( contrairement au titre et à la conclusion de l’article, voilà encore une ambigüité ) met bien en évidence les multiples insuffisances de la configuration.

En titrant sur les applications publiques, l’auteur évite au moins la divulgation du secret de l’application, puisqu’il n’y en a pas. Cela va faciliter le travail des applications malicieuses ! n’allons pas demander des données protégées comme cela ...

Il existe des informations complémentaires, connectez vous pour les voir.

Notes

[1ce qui est non seulement le cas courant mais aussi le cas ( worst case ) que l’on doit prendre en compte pour une analyse de sécurité

[2Tout comme dans le cas d’une application native de mobile et de toute application sans back-end. C’est le même problème, sauf peut-être les applications mobiles de desktop dans un espace de confiance

Proof of Possession (PoP)

  publié le par DnC

Répondant à la préoccupation exprimée dans la rubrique Authentifier l’application, la "preuve de possession" est ce dont nous avons besoin pour certifier que l’application qui présente le JWT est bien celle qui a été identifiée au moment de l’authentification.

La particularité de la PoP, telle que nous prévoyons de l’implémenter, consiste à faire créer par l’application cliente une paire de clé publique-privée à chaque procédure d’authentification.

Cependant, certifier que l’application est celle qui a été authentifiée n’est pas prouver que c’est l’application authentique.

Le terme « preuve de possession » fait référence à des mécanismes de chiffrement qui atténuent le risque de vol de jetons de sécurité et d’utilisation par un attaquant.
Dans le cas des jetons "porteur" ( Bearer Token ), la simple possession du jeton permet à une application de l’utiliser vis-à-vis d’une ressource protégée.

Le document rfc7800 - Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs) - propose une implémentation associé au JWT.
Nota : Nous sommes dans OpenID Connect. Nous remplaçons donc dans le texte :
"émetteur (issuer)" par "serveur OIDC (OpenID Provider)" ou OP,
"présentateur (presenter)" par "application cliente ou client ou RP",
Nous conservons "destinataire (recipient)" en gardant à l’esprit que ce peut être une "ressource protégée" ou RS, une API ou n’importe quel autre ressource ou encore l’OP lui-même si le jeton lui est retourné pour ré-authentification silencieuse.

Cette spécification explique comment déclarer dans un jeton Web JSON (JWT) que le client présentant le JWT possède une clé particulière preuve-de-possession et comment le destinataire peut obtenir de manière cryptographique la preuve que le présentateur possède la clé.

Le document présente deux implémentations : avec preuve de possession à clé symétrique ou asymétrique. Nous ne considérerons que la deuxième, demandant moins d’échanges et paraissant plus simple de mise en œuvre.

Le client génère une paire de clés publique / privée et envoie la clé publique à l’OP. L’OP crée un JWT contenant la clé publique (ou son identifiant).

Lorsque le client présente le JWT au destinataire, il doit produire la preuve-de-possession. Il présente un nonce signé avec la clé privée dans une déclaration ’cnf’.

Le destinataire peut vérifier qu’il interagit avec le client authentifié en extrayant la clé publique de la demande de confirmation du JWT (après vérification de la signature numérique du JWT) et en en vérifiant la signature du nonce.

Questions ...

Dans l’état actuel du développement exploratoire ( pas encore sur GitHub ), voici quelques questions :

- Pourquoi un malware ne serait-il pas capable de générer un PoP acceptable avec la technique décrite ? On obtient bien la preuve que l’application est celle avec laquelle l’utilisateur final a été authentifiée. Mais authentifié ne veut pas dire authentique. Dans le cas d’une application avec back-end, l’application peut être identifiée avec certitude comme étant le modèle original. Mais dans le cas d’une application sans back-end ( une copie de l’original fournie par le serveur ), un malware peut s’exécuter dans le même user-agent, ce qui ouvre notamment la possibilité de tromper l’utilisateur (physhing) auquel il revient d’identifier l’application.

- Cela permet-il de sécuriser un flux implicite ? La clé privée est-elle exposée par une application "sans backend" (la clé privée est calculée et utilisée une seule fois dans le même thread) ? Exposer la clé privée serait contraire au principe même de la cryptographie asymétrique et ôterait toute valeur au PoP.

Il existe des informations complémentaires, connectez vous pour les voir.