12
votes

Authentification dans les services Web reposants

Je crée actuellement un site Web que les utilisateurs peuvent afficher et modifier leurs widgets. Toutes les interactions avec les données de widget stockées sur mon serveur seront effectuées via des services Web reposants. Par exemple, si un utilisateur souhaite voir une liste de leurs widgets, le flux d'exécution serait quelque chose comme:

  1. Utilisateur 12345 Accès https://www.example.com/login.htm et authentifie et authentifie avec le serveur (dans mon cas via un fournisseur OpenID)
  2. utilisateur 12345 accède ensuite à la page https://www.example.com/widgets.htm
  3. Server répond avec une page HTML et JavaScript qui sera utilisé pour accéder à mes services Web.
  4. Lorsque la page HTML a chargé la fonction JavaScript getwidgets () sera appelée. getwidgets () appellera mon service Web https://www.example.com/services/widget/12345
  5. Le service répond avec une liste des widgets des utilisateurs dont une autre fonction JavaScript renduwidgets (widgets) mettra à jour la page HTML

    Je ne veux pas que quelqu'un d'autre mais que l'utilisateur 12345 accède à leurs propres widgets, donc je suppose getwidgets () devra fournir une authentification à mon service Web. Je ne suis pas sûr de ce que le meilleur moyen serait de réaliser cela.

    Je pensais que le client et le serveur pourraient avoir un secret partagé que getwidgets () sera envoyé au service Web. Le serveur pourrait générer ce secret comme chaîne aléatoire (numéro, GUID ou autre) et inclure-le dans l'en-tête de réponse lorsque le client demande la page HTML initiale. Le client utiliserait cette clé secrète lors de l'envoi de demandes au serveur.

    Cela sonne-t-il comme une idée sensée?

    Ceci est une exigence commune, alors existe-t-il un moyen standard d'atteindre la même chose? Autant que je sache, cela est en dehors de la portée de OpenID et de OAuth ne conviendrait pas.

    Merci d'avance.


1 commentaires

3 Réponses :


9
votes

C'est une excellente question - mais je pense que votre solution peut avoir besoin d'être un peu plus complexe que vous ne le pensez.

En général, la manière dont vous souhaitez authentifier ce type de scénario est dans une poignée de main à 2 étapes. La première étape consiste à fournir à votre application de fournir au serveur une clé privée (générée par le serveur, unique à l'application cliente) pour vous authentifier. En fait, un client valide. C'est ce qui fournit des preuves faisant autorité à votre serveur que la demande provient de logiciels qu'elle connaît et peut avoir confiance.

La deuxième étape, alors, est que lorsqu'un utilisateur va se connecter à votre demande client, ils fournissent une combinaison nom d'utilisateur / mot de passe. Ces informations, ainsi que votre clé d'application, doivent tous être envoyés au serveur via SSL.

SSL crypte les données de sorte qu'une tierce partie avec un sniffer de paquet ne puisse pas lire les données en transit et le serveur procède les suivants:

  1. vérifie que la clé d'application est valide.
  2. Valide que le nom d'utilisateur existe et est associé à l'application.
  3. chiffre le mot de passe et teste la version cryptée contre la version cryptée de la base de données, associée au nom d'utilisateur.
  4. si tout de la carte de contrôle énumérés ci-dessus, le serveur renvoie un identifiant de session, qui peut être mis dans un cookie côté client - et utilisé pour ré-authentifier l'utilisateur sur chaque demande suivante. . si des tests échouent - le serveur renvoie un 401: réponse non autorisée ou une autre erreur similaire.

    À ce stade, le client peut utiliser l'ID de session retourné sans avoir à continuer à soumettre à nouveau la clé d'application.

    Votre application

    Maintenant, dans votre cas, vous pouvez accueillir le client / serveur dans la même application et sur le même serveur. Dans ce cas, vous pouvez généralement sauter toutes les pièces tournantes autour de la clé d'application privée - et dissiper simplement les demandes de script inter-sites à la place.

    pourquoi? - parce que la chose que vous protégeez vraiment est la suivante:

    serveur A héberge votre API reposant. Les clients hôtes B, C et D du client qui s'appuieront sur l'API de Server A. Ce que vous ne voulez pas, c'est pour le client e (pas votre candidature - et malveillant) de pouvoir accéder au serveur A en contournant ou en volant les informations d'identification de l'un des autres clients.

    Si, toutefois, le client et le serveur sont hébergés au même endroit et que la même URL - c'est-à-dire que l'API reposante réside à www.yourdomain.com/api et le client réside à www.yourdomain.com / - Vous pouvez généralement simplement ne pas autoriser les demandes de type AJAX provenant de Yourdomain.com - et c'est votre couche de sécurité.

    Dans ce cas, vous devez tout simplement faire pour avoir un niveau de sécurité raisonnable:

    1. Activer SSL pour votre serveur.
    2. n'autorise que les demandes de / auth / auth / login (ou quel que soit votre login POST méthode est) à venir via SSL (en C # Ceci peut être effectué à l'aide du [ExigerHTTPS] attribut sur la méthode ou le contrôleur).
    3. rejeter toutes les demandes AJAX provenant de votre propre domaine.
    4. Utilisez une couche de cryptage dans votre cookie.

      Que faut-il contenir votre cookie?

      Idéalement, le cookie doit contenir des données cryptées à deux voies que seul votre serveur peut déchiffrer. En d'autres termes - vous pouvez mettre quelque chose comme le nom d'utilisateur de l'utilisateur ou user_id à l'intérieur de la cookie - mais à deux voies, chiffroire à l'aide de Rijndael ou d'un autre système de cryptographie - à l'aide d'un mot de passe de cryptage qui Seul votre serveur a accès à (je suggère une chaîne de caractères aléatoire).

      Puis - lorsque vous recevez des demandes suivantes avec le cookie attaché, vous pouvez simplement effectuer les éléments suivants:

      1. Si le cookie existe, essayez de le déchiffrer à l'aide de votre mot de passe privé.
      2. Si les données déchiffrées obtenues sont des ordures - jetez un 401: non autorisé réponse (ceci est un cookie altéré ou faux)
      3. Si les données déchiffrées obtenues sont un nom d'utilisateur qui correspond à votre base de données - vous savez maintenant qui apporte la demande - et peut filtrer / les servir aux données en conséquence.

        J'espère que cela vous aidera. :) Si non - n'hésitez pas à poster des commentaires et à poser des questions, et je vais essayer de clarifier.


4 commentaires

Merci pour votre réponse. J'aurai besoin de temps pour y penser et je vous recontacterai.


Prenez votre temps - je travaille sur une chose très similaire pour le travail en ce moment, c'est donc définitivement haut de gamme.


Je vous ai récompensé la Bounty comme vous étiez d'abord à commenter et il semble que quelques personnes sont d'accord avec vous. Cependant, je ne pense toujours pas avoir une réponse complète. J'ai ma propre solution pour le moment (que j'ajouterai aux réponses espece ce soir) mais je tiens à quelqu'un à dire "hé, c'est déjà fait, vérifiez cette Standard".


Eh bien, merci pour le représentant supplémentaire, très apprécié. J'ai fini de mettre en œuvre quelque chose de presque identique à ma réponse ici pour mon projet au travail et avec quelques modifications, il suffisait de passer des exigences de sécurité assez rigoureuses. C'est un peu de travail à mettre en place, cependant. Je suis heureux de vous donner des détails supplémentaires ou de discuter sur Skype ou quelque chose si vous le souhaitez. Faites-moi savoir par commentaire et je vous donnerai mes informations de contact.



2
votes

N'est-ce pas aussi simple que de Macing la demande au service Web?

Donc, vous fournissez le JavaScript qui appelle le service Web, dans ce JavaScript, vous mettez une nonce. Contrairement à la plupart des implémentations de nonce, vous en diriez pour la durée de la session utilisateur.

Lorsque vous appelez le service Web, le JavaScript dans GetWidgets () utilise la nonce comme la clé de hachage de données «aléatoires», j'utiliserais probablement le temps formaté comme une chaîne et les données, l'ID de l'utilisateur (12345). comme le sel. Le JavaScript envoie ensuite les données hachées et la chaîne temporelle sans faille dans le cadre de l'appel de service Web, mais pas la nonce. J'assurerais alors que le temps qui a été envoyé a été récent (c'est-à-dire 20 dernières secondes, limitant les attaques de replay) et que lorsque le serveur a atteint le temps avec l'identifiant de l'utilisateur en tant que sel avec la non -ce (qu'elle sait parce qu'elle l'a définie) obtient le même résultat.

Ainsi, nous avons utilisé une clé partagée définie par le serveur et envoyé au client pour agir en tant que clé de la production d'un code d'autorisation de message haché (Mac ou HMAC), mais également d'empêcher les attaques de replay (le Mac sera différent pour Chaque demande et dispose d'une fenêtre de relecture très courte) et de prévenir le détournement de la session en ne transférant aucune information de session dans des cookies.


Je ne suis pas survint de votre scénario spécifique avant, mais cela semble être un cas spécifique d'un problème générique de messages authentifiant sans vouloir envoyer des informations d'identification à chaque message. Macing est fiable comment j'ai vu cela fait.

voir: http://en.wikipedia.org/wiki/message_authentication_code , cela donne Une bonne vue d'ensemble sur Macing et comprend même un bon diagramme pour vous aider à expliquer. C'est une solution assez bien troden, la seule déviation que je recommanderais (parce que je suis tombée en faute) est d'inclure le temps dans le message pour empêcher les replays.

C'est la base de l'approche utilisée par Last.fm pour aider à autoriser l'accès à leur API, voir la partie 8 sur cette page (appels de signature): http://www.last.fm/api/authspec#8 . Le hachage qu'ils utilisent est MD5, le querystring est inclus dans le texte du corps à hacher et le secret utilisé une clé de session obtenue à partir d'un appel d'authentification initial.


2 commentaires

Merci pour la réponse. Connaissez-vous de toute source crédible qui donne une description de cela? Je ne peux pas imaginer que je suis la seule personne à avoir rencontré ce problème et je m'attendais à ce que la solution soit documentée quelque part.


J'allais récompenser la prime à vous et à Troy, mais cela semble que cela ne va qu'à une seule personne (est-ce correct?). Merci pour les informations et une fois que j'ai eu mes pensées pour que je puisse ajouter ma solution aux réponses (mais pas la marque comme une réponse). Selon le commentaire, je viens de partir sur Troys Répondre, j'espère que quelqu'un me dirige dans la direction d'une mise en œuvre standard appropriée.



-1
votes

Je ne sais pas grand chose de la sécurité, mais je pense que tout dépend de la quantité de temps / de coût que vous êtes prêt à dépenser (garder à l'esprit que tout est piratable).

En tant que préoccupée de la sécurité que vous le souhaitez, vous avez probablement protégé vos variables de session, la chose la plus facile que vous puissiez faire est un appel AJAX à une action serveur dans laquelle vous vérifiez la session et la comparez-la avec la demande de l'utilisateur.


1 commentaires

J'apprécie prendre le temps de répondre mais je ne pense pas que votre réponse ajoute quoi que ce soit à la discussion. En ce qui concerne votre premier point, oui, toute mesure de sécurité est effectivement piraillable donnée suffisamment de temps et d'efforts. L'objectif devrait être d'avoir une solution dans laquelle le temps et l'effort requis l'emporter sur le gain. Sur votre deuxième point, un service Web reposant ne devrait pas s'appuyer sur une session pour conserver l'État. C'est un principal principal. Pour référence peej.co.uk/articles/no-sessions.htmlLeight/a >