Service Web RESTful - comment authentifier les demandes d'autres services?

117

Je conçois un service Web RESTful auquel les utilisateurs doivent avoir accès, mais également d'autres services et applications Web. Toutes les demandes entrantes doivent être authentifiées. Toutes les communications ont lieu via HTTPS. L'authentification de l'utilisateur fonctionnera sur la base d'un jeton d'authentification, acquis en POSTANT le nom d'utilisateur et le mot de passe (via une connexion SSL) à une ressource / session fournie par le service.

Dans le cas des clients de service Web, il n'y a aucun utilisateur final derrière le service client. Les demandes sont initiées par des tâches planifiées, des événements ou d'autres opérations informatiques. La liste des services de connexion est connue à l'avance (évidemment, je suppose). Comment dois-je authentifier ces demandes provenant d'autres services (Web)? Je souhaite que le processus d'authentification soit aussi simple que possible à mettre en œuvre pour ces services, mais pas au détriment de la sécurité. Quelles seraient les normes et les meilleures pratiques pour un scénario comme celui-ci?

Options auxquelles je peux penser (ou qui m'ont été suggérées):

  1. Demandez aux services clients de recourir à un «faux» nom d'utilisateur et mot de passe, et de les authentifier de la même manière que les utilisateurs. Je n'aime pas cette option - ça ne me semble pas juste.

  2. Attribuez un identifiant d'application permanent pour le service client, éventuellement une clé d'application. Autant que j'ai compris, c'est la même chose que d'avoir un nom d'utilisateur + un mot de passe. Avec cet identifiant et cette clé, je peux soit authentifier chaque demande, soit créer un jeton d'authentification pour authentifier d'autres demandes. Quoi qu'il en soit, je n'aime pas cette option, car toute personne qui peut obtenir l'identifiant et la clé de l'application peut se faire passer pour le client.

  3. Je pourrais ajouter une vérification d'adresse IP à l'option précédente. Cela rendrait plus difficile d'effectuer de fausses demandes.

  4. Certificats clients. Configurez ma propre autorité de certification, créez un certificat racine et créez des certificats clients pour les services clients. Cependant, quelques problèmes me viennent à l'esprit: a) comment puis-je encore permettre aux utilisateurs de s'authentifier sans certificats et b) à quel point ce scénario est-il compliqué à mettre en œuvre du point de vue du service client?

  5. Quelque chose d'autre - il doit y avoir d'autres solutions là-bas?

Mon service fonctionnerait sur Java, mais j'ai délibérément omis des informations sur le cadre spécifique sur lequel il serait construit, car je suis plus intéressé par les principes de base et pas tant par les détails de mise en œuvre - je suppose que la meilleure solution pour cela sera être possible de mettre en œuvre quel que soit le cadre sous-jacent. Cependant, je suis un peu inexpérimenté avec ce sujet, donc des conseils et des exemples concrets sur la mise en œuvre réelle (tels que des bibliothèques tierces utiles, des articles, etc.) seront également très appréciés.

Tommi
la source
Si je peux suggérer, familiarisez-vous avec les services de sites Web à grande surface et choisissez ce que vous aimez. Vos utilisateurs trouveront également des similitudes avec les meilleures pratiques des autres services RESTful.
Yzmir Ramirez
J'ai trouvé une autre question (presque deux ans) qui touche un sujet similaire: stackoverflow.com/questions/1138831/…
Tommi
Sur quel système d'exploitation les services (à la fois le Web et les autres) sont-ils hébergés? S'exécutent-ils sur des serveurs faisant partie de la même infrastructure?
Anders Abel
Le système d'exploitation peut varier: Win, * nix etc. Et les services clients peuvent ou non être dans la même infrastructure que mon service.
Tommi

Réponses:

34

Toute solution à ce problème se résume à un secret partagé. Je n'aime pas non plus l'option de nom d'utilisateur et de mot de passe codés en dur, mais elle a l'avantage d'être assez simple. Le certificat client est également bon, mais est-il vraiment très différent? Il y a un certificat sur le serveur et un sur le client. Son principal avantage est qu'il est plus difficile de recourir à la force brute. J'espère que vous avez mis en place d'autres protections pour vous protéger contre cela.

Je ne pense pas que votre point A pour la solution de certificat client soit difficile à résoudre. Vous utilisez juste une branche. if (client side certificat) { check it } else { http basic auth }Je ne suis pas un expert Java et je n'ai jamais travaillé avec lui pour créer des certificats côté client. Cependant, un rapide Google nous conduit à ce tutoriel qui regarde droit dans votre allée.

Malgré toute cette discussion sur «ce qu'il y a de mieux», permettez-moi de souligner qu'il existe une autre philosophie qui dit: «moins de code, moins d'intelligence, c'est mieux». (Je détiens personnellement cette philosophie). La solution de certificat client ressemble à beaucoup de code.

Je sais que vous avez exprimé des questions sur OAuth, mais la proposition OAuth2 inclut une solution à votre problème appelée « jetons de support » qui doit être utilisée en conjonction avec SSL. Je pense que, par souci de simplicité, je choisirais soit l'utilisateur / pass codé en dur (un par application afin qu'ils puissent être révoqués individuellement), soit les jetons de support très similaires.

newz2000
la source
27
Les certificats clients ne sont PAS un secret partagé. C'est pourquoi ils existent. Le client a une clé privée et le serveur a une clé publique. Le client ne partage jamais son secret et la clé publique n'est pas un secret.
Tim
5
Le lien du tutoriel ne mène pas à un article du tutoriel mais à une page d'index Java sur le site Oracle ...
Marjan Venema
2
@MarjanVenema Eh bien, c'est parce que vous essayez le lien +2 ans après la réponse à newz2000, mais vous pouvez toujours essayer WayBack Machine: web.archive.org/web/20110826004236/http://java.sun.com/…
Fábio Duque Silva
1
@MarjanVenema: Je suis désolé, mais vous vous attendez à ce que newz2000 vienne ici et mette à jour le lien après sa mort? Comme vous l'avez dit, c'est la pourriture des liens, donc ça arrive tôt ou tard. Soit vous essayez d'accéder aux archives pour voir ce que l'auteur voyait à ce moment-là, soit vous trouvez le nouveau lien et apportez une contribution positive. Je ne vois pas comment votre commentaire a aidé qui que ce soit. Mais ici, suivez ce lien: oracle.com/technetwork/articles/javase/… (attention, ça finira par pourrir aussi)
Fábio Duque Silva
2
@ FábioSilva: Non. Je ne m'attends pas à ce qu'il fasse ça. J'ai lu l'archive mais je n'ai pas eu le temps d'aller chercher un nouveau lien, alors j'ai fait la meilleure chose suivante: mettre dans un commentaire qu'il est mort afin que quelqu'un d'autre de la communauté puisse trouver le nouvel emplacement et mettre à jour le Publier. Comme vous avez évidemment eu le temps de trouver le nouveau lien, pourquoi n'avez-vous pas mis à jour le lien dans le message au lieu de le mettre dans un commentaire qui me gêne?
Marjan Venema
36

Après avoir lu votre question, je dirais, générez un jeton spécial pour faire la demande requise. Ce jeton vivra à un moment précis (disons en un jour).

Voici un exemple de pour générer un jeton d'authentification:

(day * 10) + (month * 100) + (year (last 2 digits) * 1000)

par exemple: 3 juin 2011

(3 * 10) + (6 * 100) + (11 * 1000) = 
30 + 600 + 11000 = 11630

puis concaténer avec le mot de passe de l'utilisateur, par exemple "my4wesomeP4ssword!"

11630my4wesomeP4ssword!

Puis faites MD5 de cette chaîne:

05a9d022d621b64096160683f3afe804

Quand appelez-vous une demande, utilisez toujours ce jeton,

https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata

Ce jeton est toujours unique au quotidien, donc je suppose que ce type de protection est plus que suffisant pour toujours protéger votre service.

L'espoir aide

:)

Kororo
la source
1
J'aime vraiment la façon dont vous avez ajouté le jeton de sécurité dans chaque demande, mais que se passe-t-il lorsque le programmeur a déjà créé des centaines de pages jsp et qu'ensuite, il a implémenté la sécurité dans les 100 pages précédemment créées ainsi que les pages qui vont être créées. Dans ce cas, ajouter un jeton à chaque demande n'est pas un choix correct. Quoi qu'il en soit, +1 pour votre technique. :)
Ankur Verma
4
Que se passe-t-il si les horloges ne sont pas synchronisées? Le client ne générera-t-il pas le mauvais jeton dans ce cas? Même si les deux génèrent le datetime en UTC, leurs horloges pourraient toujours être différentes, ce qui entraînerait une fenêtre de temps tous les jours, lorsque le jeton ne fonctionnerait pas?
NickG
@NickG, j'ai eu ce problème avant, le seul moyen de le sécuriser en demandant l'heure du serveur. Cela éliminera à 99% le problème de l'UTC. Bien sûr, l'inconvénient est un appel supplémentaire au serveur.
kororo
mais je peux toujours utiliser votre service Web en utilisant ce jeton pendant une journée, n'est-ce pas? je ne vois pas comment cela va aider
Mina Gabriel
@MinaGabriel, vous pouvez ajouter plus de temps dans la génération de jetons. (minute * 10) + (heure * 100) + (jour * 1000) + (mois * 10000) + (année (2 derniers chiffres) * 100000)
kororo
11

Il existe plusieurs approches différentes.

  1. Les puristes RESTful voudront que vous utilisiez l'authentification BASIC et que vous envoyiez des informations d'identification à chaque demande. Leur raisonnement est que personne ne stocke aucun état.

  2. Le service client peut stocker un cookie, qui conserve un identifiant de session. Personnellement, je ne trouve pas cela aussi offensant que certains des puristes que j'entends - cela peut coûter cher de s'authentifier encore et encore. Il semble que vous n'aimiez pas trop cette idée, cependant.

  3. D'après votre description, il semble vraiment que vous soyez intéressé par OAuth2. Mon expérience jusqu'à présent, d'après ce que j'ai vu, c'est que c'est un peu déroutant et un peu avant-gardiste. Il existe des implémentations, mais elles sont rares. En Java, je comprends qu'il a été intégré dans les modules de sécurité de Spring3 . (Leur tutoriel est bien écrit.) J'attendais de voir s'il y aurait une extension dans Restlet , mais jusqu'à présent, bien que cela ait été proposé, et peut-être dans l'incubateur, il n'a toujours pas été complètement intégré.

jwismar
la source
Je n'ai rien contre l'option 2 - je pense que c'est une bonne solution dans une application RESTful - mais d'où le service client obtient-il le jeton en premier lieu? Comment s'authentifient-ils la première fois? Peut-être que je pense mal, mais il semble étrange que le service client ait besoin d'avoir son propre nom d'utilisateur et mot de passe pour cela.
Tommi
Si l'utilisateur final est un de vos utilisateurs, le service intermédiaire peut vous transmettre ses informations d'identification lors de la première demande et vous pouvez renvoyer un cookie ou un autre jeton.
jwismar
De même, dans le scénario OAuth, l'utilisateur final délègue au service intermédiaire son accès à votre service Web.
jwismar
Il semble y avoir un malentendu - il n'y a aucun utilisateur final derrière le service client . J'ai mis à jour ma question pour mieux expliquer la situation.
Tommi
1
J'ajouterais simplement que l'option n ° 1 répertoriée ci-dessus ne devrait être effectuée que sur HTTPS.
mr-sk
3

Je crois que l'approche:

  1. Première demande, le client envoie un identifiant / mot de passe
  2. Exchange id / pass pour un jeton unique
  3. Validez le jeton à chaque demande suivante jusqu'à son expiration

est assez standard, indépendamment de la façon dont vous implémentez et d'autres détails techniques spécifiques.

Si vous voulez vraiment pousser l'enveloppe, vous pourriez peut-être considérer la clé https du client dans un état temporairement invalide jusqu'à ce que les informations d'identification soient validées, limiter les informations si elles ne le sont jamais et accorder l'accès lorsqu'elles sont validées, en fonction à nouveau de l'expiration.

J'espère que cela t'aides

Dynrepsys
la source
3

En ce qui concerne l'approche du certificat client, elle ne serait pas terriblement difficile à implémenter tout en permettant aux utilisateurs sans certificats client d'entrer.

Si vous créiez en fait votre propre autorité de certification auto-signée et que vous délivriez des certificats client à chaque service client, vous disposeriez d'un moyen simple d'authentifier ces services.

Selon le serveur Web que vous utilisez, il devrait y avoir une méthode pour spécifier l'authentification client qui acceptera un certificat client, mais qui n'en exigera pas. Par exemple, dans Tomcat, lorsque vous spécifiez votre connecteur https, vous pouvez définir «clientAuth = want» au lieu de «true» ou «false». Vous vous assureriez alors d'ajouter votre certificat CA auto-signé à votre truststore (par défaut, le fichier cacerts dans le JRE que vous utilisez, à moins que vous n'ayez spécifié un autre fichier dans la configuration de votre serveur Web), de sorte que les seuls certificats de confiance seraient ceux émis hors de votre autorité de certification auto-signée.

Côté serveur, vous n'autorisez l'accès aux services que vous souhaitez protéger que si vous êtes en mesure de récupérer un certificat client à partir de la demande (non nul), et réussissez les vérifications de DN si vous préférez une sécurité supplémentaire. Pour les utilisateurs sans certificat client, ils pourront toujours accéder à vos services, mais n'auront simplement aucun certificat présent dans la demande.

À mon avis, c'est le moyen le plus «sûr», mais il a certainement sa courbe d'apprentissage et ses frais généraux, ce n'est donc pas nécessairement la meilleure solution pour vos besoins.

bobz32
la source
3

5. Autre chose - il doit y avoir d'autres solutions là-bas?

Vous avez raison, il y en a! Et cela s'appelle JWT (JSON Web Tokens).

JSON Web Token (JWT) est une norme ouverte (RFC 7519) qui définit un moyen compact et autonome de transmettre en toute sécurité des informations entre les parties en tant qu'objet JSON. Ces informations peuvent être vérifiées et fiables car elles sont signées numériquement. Les JWT peuvent être signés à l'aide d'un secret (avec l'algorithme HMAC) ou d'une paire de clés publique / privée à l'aide de RSA.

Je recommande vivement d'examiner les JWT. Ils constituent une solution beaucoup plus simple au problème par rapport aux solutions alternatives.

https://jwt.io/introduction/

justin.hughey
la source
1

Vous pouvez créer une session sur le serveur et la partager sessionIdentre le client et le serveur à chaque appel REST.

  1. Première Authentifier demande REST: /authenticate. Renvoie la réponse (selon le format de votre client) avec sessionId: ABCDXXXXXXXXXXXXXX;

  2. Conservez ce sessionIden Mapavec la session actuelle. Map.put(sessionid, session)ou utiliser SessionListenerpour créer et détruire des clés pour vous;

    public void sessionCreated(HttpSessionEvent arg0) {
      // add session to a static Map 
    }
    
    public void sessionDestroyed(HttpSessionEvent arg0) {
      // Remove session from static map
    }
    
  3. Obtenez sessionid avec chaque appel REST, comme URL?jsessionid=ABCDXXXXXXXXXXXXXX(ou d'une autre manière);

  4. Récupérer HttpSessionde la carte en utilisant sessionId;
  5. Validez la demande pour cette session si la session est active;
  6. Renvoyer une réponse ou un message d'erreur.
arviarya
la source
0

J'utiliserais pour qu'une application redirige un utilisateur vers votre site avec un paramètre d'ID d'application, une fois que l'utilisateur approuve la demande, génère un jeton unique qui est utilisé par l'autre application pour l'authentification. De cette façon, les autres applications ne gèrent pas les informations d'identification des utilisateurs et d'autres applications peuvent être ajoutées, supprimées et gérées par les utilisateurs. Foursquare et quelques autres sites s'authentifient de cette manière et c'est très facile à mettre en œuvre comme l'autre application.

Devin M
la source
Hmm, je ne sais pas si j'ai pu suivre l'explication. De quel utilisateur parlons-nous? Je parle d'une application communiquant avec une autre application. Je suppose que vous avez compris cela, mais je n'arrive toujours pas à comprendre. Que se passe-t-il lorsque ce «jeton» expire, par exemple?
Tommi
Eh bien, le jeton que vous générez et renvoyez à l'autre application est un jeton persistant, il est lié à l'utilisateur et à l'application. Voici un lien vers les documents de foursquares developer.foursquare.com/docs/oauth.html et il s'agit simplement de oauth2, alors regardez-y pour une bonne solution d'authentification.
Devin M
Il semble y avoir un malentendu - il n'y a aucun utilisateur final derrière le service client . La documentation de Foursquare que vous avez liée mentionne brièvement l'accès sans utilisateur, donc c'était au moins quelque peu utile - merci! Mais je suis toujours incapable de me faire une idée complète de la façon dont cela fonctionnerait dans la réalité.
Tommi
Générez simplement des clés pour les applications, si tout ce que vous faites est d'autoriser l'accès aux applications, un simple application_id et application_key devraient fonctionner pour l'authentification. Si vous voulez qu'ils s'authentifient avec un jeton, utilisez les options d'authentification de jeton de l'appareil, car il ne s'agirait que d'un paramètre passé avec la demande d'URL à votre application.
Devin M
Mais n'est-ce pas exactement la même chose que le scénario de jeton d'authentification de session nom d'utilisateur + mot de passe? Application_id et application_key ne sont-ils pas simplement des synonymes de nom d'utilisateur et de mot de passe? :) C'est parfaitement bien si c'est vraiment la pratique standard pour une situation comme celle-ci - comme je l'ai dit, je suis inexpérimenté à ce sujet - mais je pensais juste qu'il pourrait y avoir d'autres options ...
Tommi
-3

Outre l'authentification, je vous suggère de réfléchir à la situation dans son ensemble. Pensez à rendre votre service RESTful backend sans aucune authentification; puis placez un service de couche intermédiaire très simple d'authentification requise entre l'utilisateur final et le service backend.

Dagang
la source
Entre l'utilisateur final et le service backend? Cela ne laisserait-il pas les services client totalement non authentifiés? Ce n'est pas ce que je veux. Je peux bien sûr placer cette couche intermédiaire entre mon service Web et mes services client, mais cela laisse toujours la question ouverte: quel serait le modèle d'authentification réel?
Tommi
La couche intermédiaire peut être un serveur Web tel que Nginx, vous pouvez y effectuer une authentification. Le modèle d'authentification peut être basé sur la session.
Dagang
Comme j'ai essayé de l'expliquer dans ma question, je ne veux pas d'un schéma d'authentification basé sur la session pour ces services clients. (S'il vous plaît voir mes mises à jour de la question.)
Tommi
Je vous suggère d'utiliser une liste d'adresses IP blanche ou une plage d'adresses IP au lieu du nom et du mot de passe. Habituellement, l'adresse IP du service client est stable.
Dagang