Accueil > OpenID Connect OAuth Serveur dédié > Développer > OIDC et les Application à page unique : exemple d’une belle mascarade (...)

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

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 ?