web-dev-qa-db-fra.com

Authentification du service Web reposante

J'ai une API de service Web Restful, qui est utilisée par différents tiers. Une partie de cette API est restreinte (vous avez besoin d'un nom d'utilisateur/mot de passe pour y accéder). Je me demandais quelle serait la meilleure façon de mettre en œuvre l'authentification?

J'utilise https, donc la communication est cryptée. J'ai deux idées:

  • Avant que l'utilisateur ne commence à utiliser le service (restreint), il envoie le nom d'utilisateur/mot de passe en utilisant POST (puisque https est utilisé, les informations d'identification sont cryptées). Une fois la connexion réussie, le serveur renvoie une valeur aléatoire à usage unique) (nonce) qui est mis en correspondance avec ce nom d'utilisateur. Lors de la prochaine demande, à côté d'un nom d'utilisateur, le client envoie le nonce précédemment renvoyé. , il s'agit d'une version allégée de l'authentification d'accès Digest.
  • Étant donné que cette API est utilisée par un tiers, le nom d'utilisateur/mot de passe peut être utilisé pour chaque demande (restreinte). Puisque https est utilisé, ils seront cryptés. La chute de cette approche est le fait que ce ne serait pas conforme à Restful (POST serait toujours utilisé).

Je suis beaucoup plus proche de choisir la première approche (elle est conforme à Restful, relativement facile à implémenter, XML, json ou html peuvent être utilisés sans rien changer), mais je voulais voir quelle est votre opinion? Que recommandez-vous: première, deuxième ou quelque troisième approche?

Btw, j'utilise Python côté serveur.

43
kevin

Une façon dont j'ai vu cela dans les API (et la façon dont je l'implémente actuellement) est de créer une ressource RESTful appelée Session qui est créée via un POST qui fournit un nom d'utilisateur et un mot de passe.

Voici essentiellement comment je l'ai implémenté:

POST /sessions { Username: "User", Password: "Password" }

Créez une session limitée dans le temps et renvoie la ressource de session qui contient la valeur de clé de session et l'expiration. Vous pouvez également vouloir renvoyer cela comme une valeur de cookie pour la commodité de l'implémentation des clients API.

DELETE /session/{id}

Expire immédiatement la session afin qu'elle ne puisse plus être utilisée. Ceci est utilisé pour les déconnexions explicites.

Je demande ensuite à l'utilisateur de joindre la clé de session via un paramètre de requête, bien que vous puissiez également autoriser sa soumission via une valeur de cookie, je recommande d'autoriser les deux.

Ce que je préfère, c'est que c'est extrêmement simple.

Évidemment, votre scénario dictera quelque peu la façon dont vos sessions devraient être gérées, peut-être qu'elles ne sont pas limitées dans le temps et durent indéfiniment, et peut-être qu'elles sont hachées ou chiffrées pour plus de sécurité.

Si vous utilisez HTTPS partout, vous n'avez probablement pas à vous inquiéter trop. Cependant, si vous souhaitez utiliser HTTP, vous devrez utiliser quelque chose comme un hachage avec une clé secrète et dire un horodatage pour générer une clé sécurisée par demande. De cette façon, vous pouvez partager la clé secrète via HTTPS, puis basculer vers HTTP pour d'autres appels. Même si quelqu'un parvient à renifler la clé d'une demande, celle-ci peut expirer presque immédiatement et être inutile.

Avertissement: je ne suis pas un expert en sécurité ;-).

22
Chris Nicola

Il n'y a aucune raison de ne pas simplement utiliser l'authentification HTTP ici.

Cela dit, le concept de POSTing pour obtenir un nonce de bloc de temps peut bien fonctionner. Mais ce sont les motivations qui expliquent pourquoi vous devriez sauter à travers ce cerceau supplémentaire en premier lieu.

Cette technique a été envisagée lors de l'utilisation d'un hachage bcrypt pour le mot de passe d'origine, en raison des frais réels de validation d'un utilisateur (si vous ne le savez pas, bcrypt peut être réglé pour prendre un temps réel important pour exécuter la fonction de hachage). Le choix a été fait de fournir l'option permettant au service de se "connecter" une fois en utilisant le mot de passe qui passerait par le processus de validation coûteux via bcrypt, et obtiendrait alors un jeton bloqué en temps pour les demandes futures qui contourneraient le processus bcrypt. .

Dans le cas du processus bcrypt, utilisez l'authentification HTTP, le service fonctionnerait avec le mot de passe normal ainsi qu'avec le jeton. De cette façon, l'utilisateur pourrait toujours utiliser le mot de passe pour son service, mais cela devient tout simplement coûteux. Donc, ils PEUVENT le faire, ils NE DEVRAIENT PAS. Le service ne se soucie pas de la technique d'authentification utilisée par le client.

Le service nonce est offert en aparté pour améliorer le débit.

À part cela, c'est l'authentification HTTP standard, mais avec un nouveau schéma.

8
Will Hartung

Les services Web d'Amazon le font bien, consultez la méthodologie pour quelques idées. Essentiellement, ils amènent les clients à crypter un en-tête http spécial à l'aide de leur mot de passe.

Voici le lien:

http://docs.aws.Amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html

6
Tahbaza

En supposant que le service n'est jamais consommé dans un navigateur et que la communication est cryptée de toute façon, je ne vois aucun mal à une variante de la deuxième méthode: Ajoutez des X-Headers pour envoyer le nom d'utilisateur/mot de passe à chaque demande, par exemple:

GET /foo HTTP/1.1
Host: www.bar.com
X-MyUsername: foo
X-MyPassword: bar

Une autre idée serait d'utiliser HTTP Basic Auth et d'envoyer simplement un Authorization: Basic base64(user:password)- Header. Autrement dit, si la connexion est toujours cryptée.

4
tbi