i-TegoDocumentationAuthentification > OpenID Connect OAuth Serveur dédié > 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 œuvre 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), même s’il présente la meilleure sécurité, n’est pas une solution universelle à appliquer aveuglément.

Il faut bien comprendre que l’authentification ouvre la porte non seulement à un utilisateur, mais également à une application. Si l’utilisateur est bien authentifié, qu’en-est-il réellement de l’application ?

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 i-Tego WM

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.

Dans le contexte OpenID, un client est dénommé Relying Party (RP) ou "partie de confiance". Du point de vue de l’utilisateur final, il lui est demandé de faire confiance à l’application qui lui demande son autorisation : c’est à lui d’identifier l’application ... ou de se faire hameçonner ! 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 le cadre d’un client bien identifié, 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 de l’identification des applications est développée ici : Typologie des applications au regard de la sécurité des données. On y voit que le flux Authorization Code, s’il permet d’identifier les applications "avec back-end", ne le permet pas dans le cas des applications "sans back-end". Il existe des méthodes complémentaires qui réduisent le risque [3] d’un vol de jeton, mais qui, cependant, ne permettent pas d’authentifier une application cliente sans back-end.

Identifier 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 (du code Javascript exécuté par 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’OpenID Connect, nous ne considérerons que les "applications avec back-end" dont l’IP du serveur hôte garantit l’identité [4]. Autrement dit :

OpenID Connect n’est 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’OAuthSD offre également une option permettant la vérification 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['REMOTE_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 (Introspection Endpoint),
- 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). Un "bon" équilibreur de charge conserve l’adresse IP du client d’origine en l’insérant dans l’en-tête HTTP adressée au backend.
- Il existe un protocole permettant de communiquer l’IP du client entre proxies, voir : https://www.haproxy.com/fr/blog/preserve-source-ip-address-despite-reverse-proxies/.

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. L’appellation "anonymizing proxy" traduit mieux la fonction de tels proxies.

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 i-Tego WM

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

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 (il s’agit généralement d’une page HTML contenant du code Javascript exécuté par le 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.

Application "confidentielle"

Dans l’optique d’OIDC, 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 d’une configuration permettant 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.

Notons que les applications natives sous java (Android, iOS) peuvent masquer avec une certaine efficacité le secret de l’application. Il reste à étudier la sécurité de la transmission du secret entre le mobile et le serveur OIDC (à développer).

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 s’il n’y a pas connexion du navigateur au serveur [2].

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. Comment le code exécuté a-t-il été installé ? Est-il conforme au modèle ? La problématique de l’intégrité du logiciel est l’objet même de cette discussion sur la typologie des applications.

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 [3] ) 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.

Cependant, la conservation du secret dans un espace protégé est possible. La classe Java KeyStore ( implémentée notamment par iOS et Android ) permet de protéger les clés. Cependant, la sécurité de ce dispositif n’est bien réelle qu’avec le stockage des clés et/ou certificats sur un système physique indépendant.

- 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é. En effet, 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 (avec du code Javascript exécuté localement) 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 !.

Progressive Web App (PWA)

Une PWA se consulte comme un site web classique, depuis une URL sécurisée. Elle est donc, du point de vue de la protection des données, de type "avec back-end".

Si les données protégées sont exclusivement accédées depuis le serveur de l’application (et non depuis le navigateur), et que l’on applique le flux d’autorisation avec code, on se trouve dans une excellente configuration : la PWA offre des fonctionnalités proches de celle d’une application native sans les problèmes de sécurité que pose une application publique.

Les PWA offrent tellement d’avantages qu’elles devraient remplacer toutes les applications natives, qu’elles soient installées sur mobile ou sur desktop.

 

Certaines méthode renforcent la sécurité du flux Authorization Code utilisé par des applications sans back-end

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.

Sans pour autant assurer l’identification des applications sans back-end, il existe des méthodes renforçant la sécurité du flux Authorization Code :

- Les méthodes de "fingerprinting" ( qui sont aussi des méthodes utilisées pour le tracking ), par exemple "Canvas fingerprinting", identifient l’user-agent et la machine hôte, pas l’application. En fait, l’user-agent est une encapsulation qui fait obstacle à l’identification de l’application légitime par le serveur d’authentification.

- La méthode "Proof Key for Code Exchange, PKCE" renforce la sécurité de la délivrance des jetons par le contrôleur Token, en vérifiant que l’application qui demande les jetons est bien celle qui a initié l’authentification. Il n’y a donc pas non plus identification. De plus, cette méthode n’est pas applicable à la vérification par les ressources protégées.

- Les méthodes de type "Proof of Possession, PoP", permettent 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. Nous classerons dans cette catégorie la Liaison du jeton à la connexion TLS (TLS Token Binding).

Ces méthodes réduisent le risque [4] d’un vol de jeton, mais ne permettent pas d’authentifier une application cliente sans back-end.

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

Et l’Internet des Objets dans tout cela ?

Dans l’Internet des Objets (IOT), les objets sont des agents utilisateurs comme l’est un navigateur ou une application native. Les raisonnement faits sur la confidentialité des données échangées ne sont en rien différents dans le cas des objets : leur logiciel doit être fait avec des applications Web (ou des Progressive Web App aussi bien) et mettre en œuvre l’authentification avec Code.

Les objets peuvent aussi communiquer entre eux. S’ils le font en direct (et non en passant par un serveur, ce qui ramènerait au cas général), les canaux de communication doivent être sécurisés point à point. Une approche serait de considérer que l’objet fournisseur de données devra être sécurisé exactement comme n’importe quelle ressource protégée, et devra donc vérifier l’application cliente auprès du serveur OIDC.

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", autrement dit : les applications Web dont les Progressive Web Apps.

Ou bien encore :
On ne peut faire confiance à un user agent.

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. J’ai subi un terrible choc le jour où j’ai lu cela. La thérapie a consisté à écrire la rubrique dont cet article fait partie.

[2Cependant la connexion peut être discontinue, voir les Progressive Web Application (PWA).

[3L’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".

[4Le terme anglais utilisé le plus souvent est "mitigate" (attacks) = atténuer, réduire (les attaques).

Proof of Possession (PoP)

  publié le par i-Tego WM

Répondant à la préoccupation exprimée dans la rubrique Authentifier l’application, la technique "preuve de possession" serait-elle ce dont nous avons besoin ?

La particularité de la PoP consiste à faire créer par l’application cliente une paire de clé publique-privée à chaque procédure d’authentification.

Il s’agit de certifier que l’application qui présente le JWT est bien celle qui a été identifiée au moment de l’authentification. Cependant, certifier que l’application est celle qui a initié l’authentification de l’utilisateur final n’est pas prouver que c’est l’application authentique. La preuve de possession n’est pas la preuve de l’identité de l’application et de son intégrité ( la conformité de son code au modèle d’origine ).

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, plus universelle (ne nécessitant pas la connaissance préalable d’une clé partagée entre le client et l’OP), demandant moins d’échanges et paraissant plus simple de mise en œuvre et, par-dessus tout, mieux sécurisée.

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.

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

Notes

[1Notons toutefois que la classe Java KeyStore ( implémentées par iOS et Android ) permet de protéger les clés. Cependant, la sécurité de ce dispositif n’est bien réelle que si le client dispose d’un stockage matériel externe sécurisé.

[2Notons toutefois que la classe Java KeyStore ( implémentées par iOS et Android ) permet de protéger les clés. Cependant, la sécurité de ce dispositif n’est bien réelle que si le client dispose d’un stockage matériel externe sécurisé.

Liaison du jeton à la connexion TLS (TLS Token Binding)

  publié le par i-Tego WM

Avertissement : à ce jour (début 2021), cette technique ne semble pas opérationnelle du fait de la complexité de son intégration dans les navigateurs. Faut-il l’abandonner ou attendre encore ?

Un jeton ne devrait fonctionner que pour le client auquel il a été émis, sinon nous nous retrouvons avec une catastrophe majeure en matière de sécurité. La liaison du jeton est conçue pour corriger la faiblesse du "Jeton au porteur" (Bearer Token), en rendant le jeton inutilisable dans une connexion TLS (HTTPS) établie par un client autre que le détenteur légitime.

Mais cela n’apporte rien de nouveau au problème de l’identification de l’application et de son intégrité, puisque c’est l’application elle-même qui génère la clé et puisque c’est le navigateur qui est identifié.

Références

- OpenID Connect Token Bound Authentication 1.0 - draft 04
Cette spécification permet aux implémentations OpenID Connect d’appliquer la liaison jeton-TLS jeton au jeton ID OpenID Connect. Cela lie cryptographiquement le jeton d’identité aux connexions TLS sur lesquelles l’authentification s’est produite. Cette utilisation de la liaison de jetons protège le flux d’authentification contre les attaques de réexportation et de réexécution de jetons et de man-in-the-middle.

- OAuth 2.0 Token Binding.
La section 5.2 de ce document reste valable dans le cas d’OpenID Connect pour sécuriser le code d’autorisation dans le cas des clients Web Services :

5.2. Web Server Clients
Cette section décrit une méthode PKCE adaptée aux clients de serveur Web, qui lie cryptographiquement le code d’autorisation à un jeton Paire de clés de liaison sur le navigateur. Le code d’autorisation est lié à l’ID de liaison de jeton que le navigateur utilise pour fournir le code d’autorisation à un client de serveur Web, qui est envoyé au serveur d’autorisation en tant qu’ID de liaison du jeton référencé pendant la demande d’autorisation. Le client du serveur Web transmet le jeton ID de liaison au serveur d’autorisation lors de la création du jeton d’accès avec la demande de code d’autorisation. Cette liaison garantit que le code d’autorisation ne peut pas être lu ou rejoué avec succès sur le client du serveur Web à partir d’un navigateur différent de celui qui a créé la demande d’autorisation.

- RFC 8705
OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens

Ce document décrit un mécanisme d’authentification du client utilisant l’authentification mutuelle réalisée avec des certificats TLS qui offre de meilleures caractéristiques de sécurité que le secret du client inscrit sur l’OP.

Les jetons d’accès liés au certificat Mutual-TLS garantissent que seule la partie en possession de la clé privée correspondant au certificat peut utiliser le jeton pour accéder aux ressources associées. Une telle contrainte est parfois appelée confirmation de clé, preuve de possession ou détenteur de clé et est différente du cas du jeton porteur décrit dans la [RFC6750], où toute partie en possession du jeton d’accès peut l’utiliser. pour accéder aux ressources associées. La liaison d’un jeton d’accès au certificat du client empêche l’utilisation de jetons d’accès volés ou la relecture de jetons d’accès par des parties non autorisées.

- Mutual-TLS certificate-bound access tokens versus mutual-TLS client authentication
TBC

OpenID Connect : la bonne façon de passer l’ID de liaison

En transmettant avec le jeton JWT des informations dont l’intégrité peut-être vérifiée au moyen de la signature, OpenID Connect apporte un bénéfice essentiel par rapport à OAuth 2.0 : l’ID de liaison du jeton est incorporé à la charge utile du JWT et se trouve donc certifié par la signature.

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

Clé de vérification pour l’échange de code (PKCE)

  publié le par i-Tego WM

OAuthSD peut mettre en oeuvre le flux Authorization code + PKCE. La principale différence entre le flux Authorization code + PKCE et le flux standard est que le client ne fournit pas le secret de l’application, mais une clé de vérification.

En revanche, puisqu’il n’y a plus secret, il n’y a plus identification de l’application mise en œuvre par le client, qui est donc de type "public".
La méthode 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é de l’application au modèle original n’est pas garantie.

PKCE (prononcer « pixy » ) est un acronyme pour "Proof Key for Code Exchange" (clé de preuve pour l’échange de code).
Voir RFC 7636 : Flux de code d’autorisation avec clé de vérification pour l’échange de code (Proof of Key for Code Exchange, PKCE)

Utilité de PKCE pour les applications mobiles / natives

Lors de l’authentification, les applications mobiles / natives ( sans back-end ) peuvent utiliser le flux de code d’autorisation, mais elles requièrent une sécurité supplémentaire, car elles ne peuvent stocker en toute sécurité le secret du client qui pourrait être révélé par reverse-engineering (le secret client est lié à l’application et est identique pour tous les utilisateurs et appareils).
Les applications sans back-end peuvent utiliser un schéma d’URL personnalisé pour capturer les redirections (par exemple, MyApp ://). Ceci peut être détourné par une application malveillante, résidant sur le même user-agent, pour recevoir un code d’autorisation.

Pour pallier ce problème, OAuth 2.0 et OpenID Connect fournissent un complément au flux de code d’autorisation utilisant une clé de vérification pour l’échange de code (PKCE).

Flux de code d’autorisation + PKCE

Une application cliente mettant en œuvre le flux de code d’autorisation avec PKCE doit être du type "publique" c’est à dire être enregistrée sur le serveur avec un secret null.

L’application cliente publique crée une valeur de chaîne aléatoire (unique et ne contenant pas d’information), le code_verifier. Lorsque l’application cliente initie la demande de code d’autorisation, au lieu du secret, elle envoie au contrôleur Authorize le hash du code_verifier sous le paramètre code_challenge.

Une fois que l’utilisateur s’est authentifié et que le code d’autorisation est renvoyé à l’application cliente, celle-ci demande les jetons en échange du code d’autorisation, en incluant le paramètre code_verifier.
Si les codes correspondent, l’authentification est terminée et un access_token est renvoyé.

Création du code_challenge

Dans la demande du code d’autorisation, l’application doit transmettre le code_challenge et la méthode de codage "code_challenge_method" [1].

Partant d’une chaîne aléatoire ’code_verifier’, on obtient le code_challenge en appliquant un hachage SHA256 et un codage URL base64. Il faut alors passer code_challenge_method = ’S256’

PHP

  1.  // Prepare PKCE code_challenge
  2.         $code_verifier = substr( str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFabcdefghijklmnopqrstuvwxyz0123456789ABCDEFabcdefghijklmnopqrstuvwxyz0123456789ABCDEF"), 0, 50);
  3.         $code_challenge = strtr(rtrim(base64_encode(hash('sha256', $code_verifier, true)), '='), '+/', '-_');
  4.         $_SESSION['code_verifier'] = encrypt($code_verifier);  
  5. ...

Télécharger

Javascript

  1. function base64URLEncode(str) {
  2.     return str.toString('base64')
  3.         .replace(/\+/g, '-')
  4.         .replace(/\//g, '_')
  5.         .replace(/=/g, '');
  6. }
  7. var code_verifier = crypto.randomBytes(50);
  8. var code_challenge = base64URLEncode(code_verifier );
  9. ...

Télécharger

Constante de configuration ENFORCE_PKCE

Si la constante de configuration ENFORCE_PKCE est fixée à true, tous les clients doivent appliquer PKCE.
Si false, PKCE est appliqué seulement aux clients interrogeant Authorize avec le paramètre code_challenge.
Cette constante est fixée à False par défaut.

Erreur ’missing_code_challenge’

Si la constante de configuration ENFORCE_PKCE est fixée à true ou si le client interroge Authorize avec le paramètre code_challenge, et que le client ne fournit pas le code challenge ainsi que la méthode de codage dans la demande de code d’autorisation, le contrôleur Authorise répond avec un code 400 et le message ’missing_code_challenge’, ’This application requires you provide a PKCE code challenge’.

Appel au contrôleur Token

Lorsque l’application cliente appelle le contrôleur Token pour obtenir les jetons, elle passe le paramètre code_verifier et le client ID dans le corps de la requête POST ( au lieu de passer le client ID et le secret dans le header par la méthode d’authentification HTTP Basic ).

Exemple en PHP :

  1.     $code_verifier = decrypt(@$_SESSION['code_verifier']);
  2.  
  3.     $data = array(
  4.         'client_id' => $client_id,
  5.         'grant_type' => 'authorization_code',
  6.         'code' => $code,
  7.         'code_verifier' => $code_verifier,
  8.     );
  9.  
  10.     $h = curl_init($token_endpoint);
  11.     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  12.     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  13.     //curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  14.     curl_setopt($h, CURLOPT_POST, true);
  15.     curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  16.     curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  17.     curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  18.  
  19.     $res = curl_exec($h);
  20.     ...

Télécharger

Erreur ’code_verifier_missing’

Si le client a interrogé Authorize avec un code_challenge (quelque soit la valeur de la constante de configuration ENFORCE_PKCE), et que le client ne fournit pas le paramètre ’code_verifier’ dans la demande de jeton, le contrôleur Token répond avec un code 400 et le message ’code_verifier_missing’, "The PKCE code verifier parameter is required’.

Discussion sur la sécurité de PKCE

Tout d’abord rappelons que PKCE ne remplace pas le secret de l’application, et n’apporte donc aucune réponse à la problématique de l’identification de l’application cliente.

Le flux de code d’autorisation amélioré par PKCE introduit un code aléatoire "code_verifier", obscur (ne contenant pas d’information), créé par l’application appelante et pouvant être vérifié par le serveur d’autorisation. En outre, l’application appelante crée une valeur transformée du vérificateur de code appelée "code_challenge" et envoie cette valeur via HTTPS pour récupérer un code d’autorisation. De cette manière, un attaquant malveillant ne peut qu’intercepter le code d’autorisation et ne peut pas l’échanger contre un jeton sans le vérificateur de code.

Cependant, après avoir créé le code verifier dans la première étape du flux de code d’autorisation, l’application cliente doit le stocker quelque part pour le fournir à la deuxième étape, lors de la demande de jeton. Même si cela ne dure qu’un temps très court (de l’ordre de la seconde), le code verifier est exposé à un malware dont force est de constater qu’il a tout ce qu’il faut pour tenter d’obtenir un jeton à la place de l’application légitime. En principe, l’application légitime devrait avoir demandé le jeton immédiatement, probablement avant le malware qui butera sur l’interdiction d’utiliser deux fois le même code verifier. Sauf si le hacker est capable de jouer sur les priorités d’exécution ? Même si cela paraît difficile ou improbable, il y a peut-être là une possibilité d’exploit.

Notes

[1code_challenge_method peut prendre les valeurs "plain" ou "S256".

Canvas Fingerprinting

  publié le par i-Tego WM

La méthode Canvas Fingerprinting est une de celles qui permettraient 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.

Pour autant, cela ne permet pas d’identifier l’application et d’éliminer les malwares qui pourraient opérer sur le même user-agent.

La méthode est décrite ici : https://fr.wikipedia.org/wiki/Canva....

Cette technique, comme d’autres permettant d’identifier le navigateur et son environnement, est une méthode de suivi (tracking). A ce titre, elle est bloquée par certains navigateurs et logiciels de sécurité Web.
Où l’on voit que la finalité d’identification de l’application cliente s’oppose à la volonté des internautes de ne pas être suivis à la trace ...

Cette méthode n’est pas documentée pour OAuth 2.0 ou OIDC. Elle sera avantageusement remplacée par d’autres qui font l’objet de documents normatifs en cours d’élaboration.

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

  (publié initialement le jeudi 21 novembre 2019) par i-Tego WM

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

Une application à page unique est une application "sans back-end"

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 : une application à page unique est donc une application "sans back-end".

Les SPA pourraient (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, unique, 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 station 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 risqué.

- 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. Ainsi disparaît une des qualités reconnues du flux de code d’autorisation appliqué aux clients avec back-end.

- Il existe bien des méthodes complémentaires permettant de lier les jetons à l’user-agent ou bien à l’identifiant de connexion TLS (TLS Token Binding). Cela permet de prouver que l’user-agent ( ex. le navigateur de l’utilisateur final ) est bien celui avec lequel l’authentification a été effectuée et le jeton créé. Mais cela ne permet pas de discriminer la bonne application d’un malware exécuté sur la même liaison TLS. Dans le même ordre d’idées, la technique de preuve de possession Proof of Possession (PoP) semble conduire à la même conclusion.

En particulier, la méthode "Proof Key for Code Exchange, PKCE" , appliquée aux applications sans back-end dans le cadre du flux Authorization Code, renforce la sécurité en évitant le détournement du code d’autorisation. Avec cette technique, le secret de l’application n’est pas utilisé, ce qui permet de ne pas le divulguer. Cependant, cela interdit d’identifier l’application. PKCE permet de s’assurer que l’application qui présente le code d’autorisation est bien celle dans laquelle a été effectuée la procédure d’identification de l’utilisateur final, quelle que soit cette application.

Dans le cas d’une SPA comme dans celui de toute application sans back-end [2], il n’est pas possible de s’assurer que l’application qui présente le jeton est bien l’application attendue, et pas un malware.
De fait, il revient à l’utilisateur d’identifier l’application ! Cela va bien dans le premier paradigme (protéger les données de l’utilisateur), mais pas dans le deuxième (protéger les données de l’entité).

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

Avec les SPA, il n’est pas possible 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 imagine de conserver 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 web (tous les navigateurs modernes en intègrent un) 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 [3]

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 point terminal Authorize pour obtenir une autorisation. L’utilisateur DOIT s’authentifier auprès du point terminal Authorize. 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 à authentifier l’application, à laquelle peut se substituer un malware situé sur l’agent utilisateur.

Pourtant, la rédaction du paragraphe donne à penser que le flux de code d’autorisation fera mieux que le flux implicite. Et on évite de façon simple l’exposition du secret de l’application, et pour cause : "le client lui-même ne s’authentifie jamais auprès du serveur".

Dans ce contexte, évoquer la sécurité offerte par le flux "Authorization Code" est une mascarade : n.f. comédie hypocrite, mise en scène trompeuse.

Un article présentant le contre et concluant sur 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 met bien en évidence les multiples insuffisances de la configuration. Contrairement au titre "Securely ..." et à la conclusion de l’article, voilà encore une ambigüité.

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.

Incompétence ou mensonge de marketing ?

Notre article a été motivé par la lecture de celui-ci : https://www.ubisecure.com/single-si....

Le fil rouge de cet article est simple :

1. Les progrès des navigateurs (CORS et cryptographie) permettent à Javascript de mettre en œuvre le flux authorization code.

2. Donc, il ne faut plus mettre en œuvre le flux implicite avec les SPA, mais le flux Authorization Code.
Sous entendu : puisque vous savez bien que le flux Authorization Code est celui qu’il faut employer avec OIDC, et qu’on vous avait bien dit que le flux implicite était de sécurité douteuse, vous comprenez que tout va bien maintenant.

3. Bien sûr, on doit stocker les jetons. Il faut les encapsuler dans une classe, comme cela ils seront bien cachés.

Alors, incompétence ou intox ? Ne s’agirait-il pas de faire croire à un dispositif nouveau et créditer ainsi l’existence d’un avantage comparatif sur la concurrence ?

Pourquoi mentir ?

OpenID Connect a une bonne image, cela se vend bien. Tant pis si les données ne sont sécurisées que dans le cas des applications Web classiques. Vous voulez suivre la tendance des applications pour mobile ? Alors les marketeurs vous vendront OIDC sans vous avertir des limitations.
Mieux : ils mettront en avant les méthodes complémentaires, telles que PKCE ou POP, en laissant croire qu’elles apportent la solution au problème de l’authentification d’une l’application cliente sans back-end.

Qu’en est-il des serveurs headless ?

La configuration "serveur headless" est une architecture qui permet aux rédacteurs de produire et d’organiser du contenu, tout en fournissant aux développeurs des données structurées qui peuvent être affichées à l’aide d’un système distinct sur le front-end d’un site Web ou d’une application.

On aboutit souvent, côté front-end, à une SPA.
Mais, puisque les données sont servies par une application avec back-end, il y a moyen de demander à l’utilisateur final une authentification OIDC sur cette application, inscrite comme cliente du serveur OIDC. Si cela donne une garantie sur l’habilitation de l’utilisateur à accéder aux données, cela ne permettra pas d’identifier la SPA et ce qu’elle fait des données.

Il n’y a donc aucun espoir de sécuriser les SPA ?

Il y a une piste intéressante avec les applications installées sur mobile en mode Progressive Web App (PWA). Sous certaines conditions, le code et les données des PWA sont maintenus, côté mobile, identiques à leur modèle côté serveur. Une telle architecture SPA - Serveur Headless (voyez SPIP CMS headless + Gatsby) pourrait donc être convenablement sécurisée avec OIDC.

i-Tego vous accompagne

i-Tego (héritière de DnC) exerce un conseil et une assistance. Nous aidons nos clients à mettre en œuvre leur propre serveur OAuthSD. Nous accompagnons les Dev/Ops en leur transférant notre compétence, ce qui aboutit à n’utiliser OIDC que dans des configurations sécurisées.
Et nous reconnaissons à sa juste valeur le travail des IT pour protéger l’accès au réseau, et créer ainsi "l’espace de confiance" requis pour la bonne application d’OpenID Connect.

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 pour les applications de desktop dans un espace de confiance

[3Qui peut traduire "Health Relationship Trust Profile" ? Google Translator donne "Profil de confiance des relations de santé". Ce titre incompréhensible aurait-il pour but de donner un aspect pseudo-scientifique à un texte par ailleurs bien obscur ?