web-dev-qa-db-fra.com

Sécurisation d'une application JavaScript à page unique avec un backend RESTful

Je suis actuellement en train de construire un SPA JavaScript et j'ai recherché comment le sécuriser. Il existe actuellement une API RESTful qui est complètement interagie avec AJAX. Nous avons également des clients mobiles qui interagissent avec cette API, et actuellement elle ne prend en charge que l'authentification HTTP BASIC sur SSL. L'application JavaScript communiquera également exclusivement via SSL, mais BASIC Auth ne le coupera pas car cela impliquerait de stocker le mot de passe (ou un dérivé de celui-ci) sur le client. Enfin, l'application SPA sera purement JavaScript et HTML, servie sur le même serveur que l'API RESTful, mais sans framework côté serveur.

Objectifs :

  • Pas de framework côté serveur pour le client javascript (c'est juste un autre client).
  • Maintenir l'apatridie de l'API RESTful, pour les raisons typiques (évolutivité, tolérance aux pannes, déploiement simplifié, etc.)
  • Tout état doit être maintenu par le client. Aux fins de cette question, cela signifie les informations d'identification de connexion.
  • L'état de connexion maintenu par le client doit être sécurisé et résistant aux détournements de session et aux attaques similaires.

Ce que j'ai trouvé est basé sur mes recherches sur OAuth et les schémas similaires (Amazon, etc.).

  1. L'utilisateur se connectera en utilisant et HTTP POST over SSL.
  2. Le serveur calculera un hachage comme suit:

    HMAC (clé, userId + ":" + ipAddress + ":" + userAgent + ":" + todaysDateInMilliseconds)

  3. Ce jeton sera retourné au client et fourni avec chaque demande ultérieure à la place du nom d'utilisateur et du mot de passe. Il sera très probablement stocké dans localStorage ou un cookie.

Est-ce sécurisé? Ma motivation pour choisir le userId, ipAddress, todaysDateInMilleseconds est de créer un jeton qui n'est valide que pour aujourd'hui, mais qui ne nécessite pas de recherche de base de données pour chaque demande ET peut être stocké en toute sécurité sur le client. Je ne peux pas croire que la clé ne sera pas comprimée, d'où l'inclusion de l'adresse IP dans une tentative d'empêcher le détournement de session.

Permettez-moi d'inclure le lien suivant à partir d'un article lié sur StackExchange, car je pense qu'il résout un grand nombre des problèmes que j'essaie de résoudre: ID de session REST et sans état

Après les premiers commentaires ici, j'ai décidé d'utiliser uniquement les deux premiers octets de l'adresse IP pour mieux gérer les clients derrière les proxys et les clients mobiles. Ce n'est toujours pas parfait, mais c'est un compromis pour une sécurité supplémentaire.

68
Jon Wingfield

Le service offert par le jeton est que le serveur reconnaîtra en quelque sorte le jeton comme l'un des siens. Comment le serveur peut-il valider un jeton basé sur HMAC? En le recalculant, en utilisant sa clé HAMC secrète et les données sur lesquelles HMAC opère . Si vous souhaitez que votre jeton soit calculé sur l'ID utilisateur, le mot de passe, l'IP et la date, le serveur doit connaître toutes ces informations. Cependant, vous ne souhaitez pas que votre serveur stocke le mot de passe et le client ne le renverra pas à chaque demande. Comment votre système peut-il alors fonctionner?

L'idée de base, cependant, est saine:

  • Les utilisateurs "se connectent" par n'importe quelle manière que vous jugerez utile.
  • Lors de cette connexion, le serveur envoie une valeur de cookie, à renvoyer à chaque demande ultérieure (c'est ce que font les cookies).
  • Le cookie contient l'ID utilisateur, la date à laquelle il a été émis et une valeur m = HMAC (K, ID utilisateur || date || IP) .
  • Lorsque le serveur reçoit une demande, il valide le cookie: l'ID utilisateur et la date proviennent du cookie lui-même, l'IP source est obtenue à partir de la couche serveur Web et le serveur peut recalculer la valeur m pour vérifier qu'il correspond à celui stocké dans le cookie.

Vous pouvez remplacer l'intégralité du cookie par un ID de session aléatoire, si le serveur dispose d'un espace de stockage (temporaire). En effet, le serveur peut se souvenir du mappage d'un ID de session aléatoire aux informations spécifiques à l'utilisateur (comme son nom et son adresse IP); l'ancien ID de session peut expirer automatiquement, de sorte que l'espace de stockage n'augmente pas indéfiniment. Le cookie décrit ci-dessus est juste un moyen de décharger le stockage sur le client lui-même.

Remarque: l'utilisation de l'adresse IP peut impliquer des problèmes pratiques. Certains clients sont derrière des proxys, même des proxys à charge équilibrée, donc non seulement l'adresse IP du client est peut-être "cachée" (du serveur, vous voyez l'adresse du proxy, pas l'adresse du client), mais l'adresse IP que vous obtenez côté serveur pourrait se déplacer de façon irrégulière (si deux requêtes successives du client sont passées par des mandataires distincts dans une batterie de serveurs proxy).

30
Thomas Pornin

Il existe une solution beaucoup plus simple:

tilisez SSL sur tout le site, puis utilisez le suivi de session standard de votre framework.

C'est tout ce que vous devez faire.

Plus en détail, l'utilisateur se connecte initialement en fournissant son nom d'utilisateur et son mot de passe; il est POSTÉ au serveur, qui peut vérifier sa validité. Si l'authentification réussit, le code de votre serveur définit un indicateur dans l'état de session en se souvenant que l'utilisateur s'est correctement authentifié et en se souvenant de son nom d'utilisateur. Toutes les demandes ultérieures seront sur la même session, vous pouvez donc facilement les authentifier et leur permettre de continuer.

(Encore plus de détails: chaque fois que vous recevez une demande, vous vérifiez l'état de la session pour voir si l'utilisateur s'est correctement authentifié et est autorisé à effectuer cette action. Si oui, vous autorisez la demande et effectuez l'action; sinon, vous redirigez l'utilisateur à une page de connexion ou présenter un autre message d'erreur.)

Notez que cela répond à toutes vos exigences. Il ne nécessite pas de recherche de base de données à chaque demande. Il est compatible avec une API RESTful. Il est compatible avec une application d'une seule page. Vous n'avez pas besoin de coder quoi que ce soit de fantaisiste: vous utilisez simplement le support existant de votre framework pour les sessions (généralement, il utilise un cookie de session avec un ID de session unique, et un mécanisme pour stocker l'état côté serveur associé à cet ID de session) .

Dans le cadre de l'utilisation de SSL sur tout le site, vous devez définir l'indicateur secure sur votre cookie d'ID de session, pour le protéger des écoutes (cela garantira qu'il ne sera jamais envoyé via HTTP). Vous devez également activer HSTS pour indiquer au navigateur de toujours utiliser SSL sur votre site. Recherchez sur ce site pour plus d'informations sur la façon de déployer SSL sur tout le site.

Vous ne devez pas vous fier à l'adresse IP du client pour être statique. Cela peut changer, par exemple, si le client est mobile et passe d'un réseau sans fil à un autre. Par conséquent, il est préférable d'éviter d'utiliser l'adresse IP du client pour quoi que ce soit.

9
D.W.

Vérifiez cet article tilisation d'OAuth2 dans HTML5 Web App . La réponse @jandersen donne une bonne explication sur l'utilisation du flux informations d'identification du mot de passe du propriétaire de la ressource dans les applications d'une seule page. Dans tous les cas, le flux préféré pour ces applications doit être le subvention implicite . En fait, la réponse de @jandersen sur le poste référencé concerne la modification des informations d'identification du mot de passe du propriétaire de la ressource pour qu'elle se rapproche de la subvention implicite.

3
Daniel Cerecedo