Sécuriser une API REST (3/3) : gestion du JWT coté client

Commentaires

  1. Merci pour cette série d’articles très instructifs.

    J’ai cependant un doute sur la robustesse de la solution utilisant « CSRF token + Cookie HttpOnly » face aux attaques XSS. Si un attaquant parvient à injecter du JS, il n’a certes pas directement accès au JWT mais a accès au token CSRF. Comme les cookies sont automatiquement passés, il lui suffit alors de récupérer le csrf-token et de forger une requête HTTP avec fetch(). Ainsi, le token CSRF **et** le JWT sont passés et valides.

    Qu’en dites-vous ?

    Cordialement,

    1. Je m’attendais à cette question bien vue 😉 ! Il est effectivement possible de procéder à une attaque XSS et d’injecter du code effectuant une requête HTTP via fetch. C’est pourquoi il faut à tout prix se prémunir des failles XSS, comme je l’ai dit je compte faire un article qui s’intéresse plus en détail aux différents soucis de sécurité dont notamment les failles XSS (OWASP Top Ten). Mais même en étant protégé contre les failles XSS, on n’est pas à l’abri d’utiliser une librairie vérolée. Bon après vu que la méthode présentée dans l’article empêche de voler le JWT, la librairie vérolée ne pourra effectuer que des requêtes sur notre API en utilisant le token CSRF + le JWT, autrement dit elle doit être conçue uniquement pour effectuer une attaque sur notre API, donc peu de chance que ça arrive.

    1. Tu peux te le faire voler via une attaque XSS et le hacker peut l’utiliser pour récupérer un nouveau JWT. Tu peux par contre toujours demander l’ancien JWT (contenu dans un cookie) en plus du refresh token pour récupérer un nouveau JWT

  2. Merci pour ta réponse aussi rapide , enfaîte je développe une appli pour une certification de développeur web et j’utilise ta technique pour la partie « faille de sécurité » et du coup j’ai des probleme avec le refresh token , quand je signe a nouveau le acces token , je dois recupéré le xsrfToken via la requête le local storage ?

    1. Pour le refresh token tu n’as pas besoin d’envoyer le xsrfToken. Quand tu veux récupérer un nouveau refresh token tu as juste à envoyer, coté front, une requête POST sur ta route (en envoyant bien le cookie avec l’option withCredentials à true) et récupérer le refresh token depuis le cookie coté back. Tu vérifies que celui-ci est valide et tu génères les nouveaux tokens : access et refresh dans des cookies, et xsrf dans le corps de la réponse HTTP que tu stockeras dans le localStorage coté front.

  3. Du coup je ne vois pas l’utilité du refresh TOKEN, ormi etre stocké dans la BDD …
    tu vas t’en servir pour faire quoi

    1. L’access token peut avoir une durée de vie de quelques heures voir de quelques minutes, tu t’imagines bien que tu ne vas pas demander à chaque fois à l’utilisateur de se reconnecter. Par contre, une fois l’access token expiré ton application va envoyer le refresh token à l’API pour demander un nouveau access token (et un nouveau refresh token). Tu vas me dire pourquoi ne pas utiliser un access token avec une durée de vie plus élevée ? Tout simplement car celui-ci est envoyé à chaque appel API et il n’est pas impossible que celui-ci soit intercepté (peu importe la façon) et soit utilisé à des fins malveillantes. Comme nous sommes dans une API stateless, il n’y a pas de session côté serveur et l’access token n’est donc pas stocké, de ce fait il est impossible pour le serveur de blacklister cet access token « corrompu » c’est pourquoi on définit généralement des access tokens avec une durée de vie faible pour pouvoir « renouveler » ceux-ci régulièrement via le refresh token. Le refresh token n’est pas à l’abri d’être volé mais le front est censé le stocker dans un endroit sécurisé et l’envoyer uniquement pour demander un nouveau access token. De plus, si on a un doute sur un refresh token on peut très bien le blacklister côté serveur car celui-ci est stocké en base de données.

      1. Hello,
        je tombe sur cet article longtemps après sa parution mais on sait jamais.
        Si tu stockes le refresh token dans un cookie il va être envoyé automatiquement par le navigateur.
        Dans ce cas on perd l’intérêt du refresh token qui ne devrait être envoyée uniquement au moment de générer un nouvel access token (afin d’éviter au maximum que le refresh token se fasse intercepter).

        1. Hello,

          C’est vrai que l’article date un peu mais je suis toujours là pour répondre 😀 ! Je ne l’ai pas forcément bien explicité dans l’article, mais le refresh token se trouve dans un cookie qui ne sera envoyé que sur la route « /token » (via la propriété path du cookie). Donc, pas d’inquiétude, le refresh token est bien envoyé uniquement lors de la demande d’un nouvel access token. 😉

  4. Bonjour,
    Merci pour cette série d’articles très intéressante !
    Je comprends bien le principe d’access et refresh token. Mais vu le role primordial et stratégique du refresh token (il permet de régénérer un access token) je ne comprends pas qu’il soit stocker dans les cookies alors que l’article précise qu’ils peuvent être sujets à une attaque.
    Si un hacker récupère le refresh token via une attaque CRSF on peut imaginer qu’il pourra le récupérer tout le temps. Et donc accéder tout le temps a un access token. Et toute la sécurité de l’API tombe à l’eau. Non ? Je manque quelque chose ?

  5. Très belle série d’articles.
    Cependant je ne comprends pas pourquoi le refreshToken est stocké uniquement dans les cookies. Vous écrivez que dans ce même article que les cookies sont sujets aux attaques du fait de la faille CSRF. Ainsi notre refreshToken est vulnérable. Et ce refreshToken est critique dans la sécurisation de notre API. S’il est volé on peut générer un nouvel accessToken et récupérer un nouveau refreshToken. Donc accéder à l’API comme bon nous semble.
    Je manque quelque chose ?

    1. Merci !
      Attention l’attaque CSRF ne permet pas de voler les tokens mais de tromper l’utilisateur en lui faisant effectuer des actions à son insu. Le refresh token se trouve dans cookie qui ne peut être envoyé que sur la route « /token » (via la propriété path du cookie, j’aurais dû être plus précis dans l’article) qui est la route pour récupérer un nouveau access token (et refresh token). Donc même si l’on est victime d’une attaque CSRF avec le refesh token, la seule action possible est de régénérer un access token et un refresh token, ce qui en soit n’est pas très grave car je le rappelle tout se passe dans le navigateur de l’utilisateur et donc l’attaquant n’a aucun moyen de récupérer les tokens ou d’effectuer d’autres actions.

      J’espère que cela a répondu à ta question 😉

  6. Hello, étant en fin de formation DWWM, j’avais essayé de mettre en place ce type de sécurité sur une app avec une api sous symfo et un front sous React. Mais en prenant un peu de recul je me rend compte que tout ça est inutile, non ?

    Je m’explique, les failles xss sont gérés par symfony, impossible pour un utilisateur d’insérer un script en BDD, du coup on pourrait très bien se contenter de stocker le JWT ( généré à la connexion de l’utilisateur) dans le localStorage ?

  7. Salut, merci beaucoup pour ton tuto,
    Par contre j’ai un petit problème je n’arrive pas à voir le cookie dans le navigateur, pourtant credentials: ‘include’ est bien activé, ça peut être du à quoi d’autre ? :s

    1. Salut,

      J’ai le même problème. Mon API est en HTTPS.
      Quand j’utilise l’inspecteur, je le vois bien passer lors du login dans « set-cookie’ mais je ne le retrouve pas dans l’onglet cookie ensuite. Je ne peux donc pas m’en servir plus tard.

  8. Salut mon app sauvegarde en localStorage, j’ai souhaité essayer quelque chose moitié cookie moitié localStorage mais étrange quand mon app vuejs3 se connecte via axios, impossible d’avoir un cookie,

    sur la meme route de l’api j’accede et je crée un cookie mais avec axios toujours undefined

    console.log(req.cookies) // avec axios toujours undefined & en accedant via chrome j’obtiens à la prochaine requete le timestamp

    Sais tu pourquoi ?
    res.cookie(« test », new Date().getTime(), { httpOnly: true, secure: true })

  9. Bonjour,

    Je ne peux pas supprimer mon commentaire précedent en revanche j’ai résolu le probleme des cookies et donc j’ai modifié mon authentification pour que cela fonctionne comme ce que tu as proposé comme explication.

    Cela dit, dans mon entreprise j’ai voulu analyser le risque que tu as cité, je t’expose la conception par écrit et j’aimerais avoir ton avis si il y a un risque et lequel ?

    Il n’y a pas de cookies du tout !
    C’est une app VueJS3 avec Pinia et bien sure Axios, inutile d’en citer d’avantage
    Lors de l’authentification, un seul token est sauvegardé en localStorage c’est le refreshToken, l’authToken est conservé dans le store de Pinia.
    Si on rafraichit ou bien on ferme et réouvre le navigateur, l’authToken est bien entendu inexistant puisqu’il etait dans le store, la procédure est la suivante
    Au montage du composant, le refreshToken qui est valide demande à l’API un AuthToken qui sera à nouveau conservé dans le store de Pinia, si nécessaire le refreshToken se met à jour ce qui est le cas

    Vois tu une potentiel vulnérabilité ?

    Merci

Post a Comment

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.