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. Si vous installez une porte blindée sur la facade de votre maison, et que le voleur passe par la petite porte sur la cour, accusera-t-on le fabricant de la porte blindée ?

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.

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 que présentent certains flux pour la sécurité des données confidentielles.

Application de confiance, espace de confiance

Un des dangers d’une compréhension superficielle d’OIDC réside dans le fait qu’il est impossible dans certaines configurations d’authentifier l’application. La sécurité des données, 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.

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.

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

Il n’y a pas de possibilité de définir une URL de rappel, et donc pas d’authentification possible de l’application. C’est à dire que l’on ne peut pas s’assurer que le code qui est exécuté est bien le code original, ce qui ouvre la porte aux malwares.

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.

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) [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 serveur 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".

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

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

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

Voir : https://medium.com/@robert.broeckelmann/securely-using-the-oidc-authorization-code-flow-and-a-public-client-with-single-page-applications-55e0a648ab3a

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

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

  publié le 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 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].

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 soit nécessaire de fournir 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. $authorization = "Authorization: Bearer ". $res1['id_token'];
  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 de lui 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.

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.
- On devra vérifier que le proxy de confiance ne retransmet que l’IP réelle de la requête par la déclaration HTTP_X_FORWARDED_FOR, à moins qu’un mécanisme identique permette à ce proxy de reconnaître les proxies de confiance.
- 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. Nous sommes en présence de 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 : .

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

Authentifier une application cliente : Canvas Fingerprinting

  publié le par DnC

Il convient de s’assurer que l’instance de l’application interrogeant depuis une page Web une ressource protégée est bien celle à partir de laquelle l’authentification OIDC a été effectuée.
La technique Canvas Fingerprinting permet à OAuthSD d’authentifier une application cliente de type "avec front-end".

Avertissement : fonctionnalité en cours de développement.

L’authentification de l’application cliente fait l’objet de différents dispositifs intégrés dans le controleur Authorize.

La technique Canvas Fingerprinting est l’une d’elles.
Sans cette technique, l’authentification des applications clientes se limite aux applications "avec back-end".
Nous pouvons maintenant authentifier les applications "avec front-end" dont les pages HTML5 ont une origine de confiance. Le périmètre des attaques s’en trouve réduit, mais est-il réduit à néant ?
...