Authentification dans l'application Next.js (SSR SPA avec de longues sessions)
Nous développons actuellement une application Next.js (rendu côté serveur) et recherchons des moyens sécurisés pour garder les utilisateurs connectés pendant de plus longues périodes.
AFAIK cela peut être fait en utilisant une authentification silencieuse ou des jetons d'actualisation. Remarque générale: lorsqu'un utilisateur n'est pas encore connecté, nous pouvons rediriger l'utilisateur vers une page de connexion. Si l'utilisateur entre ses informations d'identification, nous utilisons le code d'autorisation (à ma connaissance, PKCE n'est pas nécessaire dans ce cas car tout est côté serveur pendant ces étapes) qui redirigera et répondra avec un code d'autorisation. Nous pouvons ensuite échanger ce code d'autorisation avec un jeton d'accès (et un jeton d'actualisation) en utilisant un secret client (tout côté serveur).
Actualiser les jetons
Étant donné que tout stockage côté client (stockage local, cookies, etc.) n'est pas sûr (attaques XSS) pour stocker tout type de jetons (en particulier les jetons d'actualisation), nous nous demandons s'il est généralement sûr de stocker un jeton d'actualisation (et un jeton d'accès) dans un cookie HTTP uniquement compte tenu du fait que…
- … Les valeurs de jeton sont chiffrées, par exemple AES, avec un secret qui n'est pas exposé côté client.
- … Les jetons d'actualisation tournent, donc lorsque vous récupérez un nouveau jeton d'accès avec votre jeton d'actualisation, vous recevez également un nouveau jeton d'actualisation. L'ancien jeton d'actualisation est invalidé et s'il est utilisé à nouveau, tous les jetons d'actualisation sont invalidés.
- … Le jeton d'actualisation expire automatiquement après quelques jours, par exemple 7 jours.
Authentification silencieuse
Une alternative possible pourrait être l'authentification silencieuse via une demande d'authentification côté serveur (invite = aucune). La session d'authentification pour l'authentification silencieuse serait également stockée dans un cookie HTTP uniquement.
-
Dans les deux scénarios, il est probablement nécessaire de s'assurer que le client ne connaît aucun de ces jetons (vous pouvez potentiellement utiliser une authentification silencieuse côté client à l'aide d'un iframe (le domaine est le même, juste des sous-domaines différents) mais le client recevrait alors potentiellement un nouveau jeton d'accès qui doit être stocké en mémoire (vulnérabilité XSS potentielle)).
Comme il s'agit d'un SPA rendu côté serveur, le côté client doit toujours être en mesure d'obtenir de nouvelles données du serveur d'API à l'aide du jeton d'accès. Pour cela, nous pensions utiliser les routes API Next.js comme proxy: donc, si le client veut obtenir de nouvelles données, il enverra une requête AJAX à la route API Next.js respective. Le contrôleur de cette route API Next.js est capable de lire et de décrypter le cookie HTTP uniquement et peut donc envoyer la requête au serveur API avec un jeton d'accès valide dans l'en-tête HTTP. Juste avant l'expiration du jeton d'accès de courte durée, le contrôleur devrait d'abord envoyer une demande au serveur d'authentification pour récupérer un nouveau jeton d'accès (et actualiser), puis continuer à envoyer la demande avec le nouveau jeton d'accès au serveur API.
Bien que cela semble bon et faisable en théorie, nous nous interrogeons sur les points suivants:
- Est-il généralement sûr d'enregistrer une actualisation (rotative) et un jeton d'accès dans un cookie HTTP uniquement? La valeur du cookie doit-elle être chiffrée ou est-ce inutile? Un jeton d'actualisation rotatif offre-t-il une sécurité supplémentaire dans ce cas?
- La méthode «Next.js API route as a proxy» est-elle un moyen sûr de s'assurer que le côté client peut obtenir de nouvelles données du serveur API? Si par exemple otherdomain.com essayait d'envoyer une requête à la route API Next.js («non protégée»), il ne répondrait avec aucune donnée car il s'agit d'un domaine différent et les cookies HTTP uniquement ne sont donc pas accessibles, n'est-ce pas? CSRF est-il possible pour ces routes d'API Next.js?
- Est-il sûr si le cookie HTTP uniquement pour le jeton d'actualisation est partagé entre tous les sous-domaines et n'est pas lié à un sous-domaine spécifique (application)? Cela nous permettrait d'accéder au cookie à partir, par exemple, du site Web réel ou d'autres sous-domaines.
- L'approche du jeton d'actualisation est-elle meilleure / plus sûre que l'approche d'authentification silencieuse?
Question de suivi: L'approche du jeton d'actualisation peut-elle également être utilisée pour authentifier les utilisateurs dans une extension de navigateur? Alors:
- L'utilisateur se connecte (Authorization Code Grant with PKCE): L'invite / la page de connexion est affichée dans une fenêtre contextuelle (ou un nouvel onglet) et la communication (code d'autorisation) se fait via postMessage.
- Le script d'arrière-plan reçoit le code d'autorisation et l'échange contre un jeton d'accès et un jeton d'actualisation rotatif (ce qui est probablement nécessaire dans ce flux (?)) À l'aide du code et d'un vérificateur de code. Ces jetons peuvent ensuite être enregistrés dans le stockage Chrome. Nous pouvons potentiellement également chiffrer les jetons, mais je ne suis pas sûr que cela offre une protection supplémentaire (?) Étant donné que le script d'arrière-plan n'est pas le même qu'un serveur.
- Si l'extension Chrome souhaite recevoir des données du serveur API, elle envoie un message au script d'arrière-plan qui enverra ensuite la demande d'API en utilisant les jetons enregistrés dans le stockage Chrome.
Réponses
question 1
Mettre le jeton d'authentification dans un httpOnlycookie, c'est bien.
Mais le cookie doit également avoir les indicateurs secureet SameSite.
Le chiffrement de la valeur du cookie n'améliore pas la sécurité. Le client n'a pas besoin de connaître la valeur en clair, il ne soumet que la valeur cryptée et le serveur autorise l'authentification basée sur cette valeur. La valeur cryptée est donc le secret.
Si un attaquant a volé le cookie chiffré, il n'a pas besoin de le déchiffrer, il lui suffit de l'envoyer directement au serveur.
Générer un jeton aléatoire de 16 octets (imprévisible) suffit, pas besoin de chiffrer.
Votre jeton d'actualisation semble être inutilement. Vous pouvez simplement utiliser le jeton de session et faire en sorte que le serveur le renouvelle à chaque fois qu'il est soumis et accepté par l'utilisateur.
De cette façon, le jeton de session est un secret "brûler après lecture" qui ne peut pas être utilisé deux fois.
Vous pouvez toujours implémenter le mécanisme dans lequel tous les jetons de session sont invalidés si un ancien est soumis (donc si un attaquant a volé un jeton, il perdra l'accès après la prochaine actualisation de l'utilisateur).
Le jeton de session peut être automatiquement soumis toutes les minutes pour limiter fortement les fenêtres d'attaque pour utiliser une fenêtre volée.
L'expiration est un élément clé pour la sécurité de la conception. L'idée est de savoir "combien de fois mon utilisateur a laissé sa session déverrouillée avec une éventuelle tentative d'accès physique malveillant?".
Il n'y a pas de réponse absolue sur la valeur correcte. Si votre application gère des données bancaires, l'expiration doit être d'une heure. S'il ne gère que les données sur les ponts de foyer aimés du propriétaire ... 7 jours, c'est ok.
Les meilleures pratiques recommandent d'utiliser des jetons non persistants (ils expirent lorsque le navigateur / l'application est fermé).
question 2
Ce schéma semble être inutilement compliqué. La simple utilisation d'un jeton de session en tant que clé API devrait suffire si le serveur gère les autorisations de l'utilisateur associé à son jeton de session.
question 3
L'utilisation du même cookie sur tous les sous-domaines avec un caractère générique (* .mondomaine.com) est généralement déconseillée. Mais il est normal de mentionner explicitement chaque sous-domaine.
L'article suivant ajoute une certaine considération aux meilleures pratiques concernant la portée des cookies: https://www.acunetix.com/blog/articles/why-scoping-cookies-to-parent-domains-is-a-bad-idea/
Question 4
Les deux approches semblent avoir le même degré de sécurité.