Je développe une API REST qui nécessite une authentification. Étant donné que l'authentification elle-même se produit via un service Web externe sur HTTP, j'ai pensé que nous distribuerions des jetons pour éviter d'appeler à plusieurs reprises le service d'authentification. Ce qui m'amène à ma première question:
Est-ce vraiment mieux que d'exiger simplement que les clients utilisent l'authentification de base HTTP à chaque demande et la mise en cache des appels côté serveur du service d'authentification?
La solution Basic Auth a l'avantage de ne pas nécessiter un aller-retour complet vers le serveur avant que les demandes de contenu puissent commencer. Les jetons peuvent potentiellement avoir une portée plus flexible (c'est-à-dire n'accorder des droits qu'à des ressources ou des actions particulières), mais cela semble plus approprié au contexte OAuth que mon cas d'utilisation plus simple.
Actuellement, les jetons sont acquis comme ceci:
curl -X POST localhost/token --data "api_key=81169d80...
&verifier=2f5ae51a...
×tamp=1234567
&user=foo
&pass=bar"
Les api_key
, timestamp
et verifier
sont requis par toutes les demandes. Le "vérificateur" est renvoyé par:
sha1(timestamp + api_key + shared_secret)
Mon intention est de n'autoriser que les appels provenant de parties connues et d'éviter que les appels ne soient réutilisés textuellement.
Est-ce suffisant? Underkill? Overkill?
Avec un jeton en main, les clients peuvent acquérir des ressources:
curl localhost/posts?api_key=81169d80...
&verifier=81169d80...
&token=9fUyas64...
×tamp=1234567
Pour l'appel le plus simple possible, cela semble horriblement verbeux. Étant donné que la shared_secret
volonté finira par être intégrée (au minimum) dans une application iOS, à partir de laquelle je suppose qu'elle peut être extraite, est-ce même offrir quelque chose au-delà d'un faux sentiment de sécurité?
la source
Réponses:
Permettez-moi de tout séparer et de résoudre chaque problème de manière isolée:
Authentification
Pour l'authentification, baseauth a l'avantage d'être une solution mature au niveau du protocole. Cela signifie que de nombreux problèmes «pourraient surgir plus tard» sont déjà résolus pour vous. Par exemple, avec BaseAuth, les agents utilisateurs savent que le mot de passe est un mot de passe afin de ne pas le mettre en cache.
Charge du serveur d'authentification
Si vous distribuez un jeton à l'utilisateur au lieu de mettre en cache l'authentification sur votre serveur, vous faites toujours la même chose: mettre en cache les informations d'authentification. La seule différence est que vous confiez la responsabilité de la mise en cache à l'utilisateur. Cela semble être un travail inutile pour l'utilisateur sans gains, je recommande donc de gérer cela de manière transparente sur votre serveur comme vous l'avez suggéré.
Sécurité de la transmission
Si vous pouvez utiliser une connexion SSL, c'est tout ce qu'il y a à faire, la connexion est sécurisée *. Pour éviter les exécutions multiples accidentelles, vous pouvez filtrer plusieurs URL ou demander aux utilisateurs d'inclure un composant aléatoire ("nonce") dans l'URL.
Si cela n'est pas possible et que les informations transmises ne sont pas secrètes, je recommande de sécuriser la demande avec un hachage, comme vous l'avez suggéré dans l'approche par jeton. Étant donné que le hachage fournit la sécurité, vous pouvez demander à vos utilisateurs de fournir le hachage comme mot de passe de base. Pour une meilleure robustesse, je recommande d'utiliser une chaîne aléatoire au lieu de l'horodatage comme "nonce" pour éviter les attaques de relecture (deux requêtes légitimes pourraient être faites pendant la même seconde). Au lieu de fournir des champs séparés "secret partagé" et "clé api", vous pouvez simplement utiliser la clé api comme secret partagé, puis utiliser un sel qui ne change pas pour empêcher les attaques de table arc-en-ciel. Le champ du nom d'utilisateur semble également être un bon endroit pour mettre le nonce, car il fait partie de l'authentification. Alors maintenant, vous avez un appel clair comme celui-ci:
C'est vrai que c'est un peu laborieux. En effet, vous n'utilisez pas de solution de niveau protocole (comme SSL). Il peut donc être judicieux de fournir une sorte de SDK aux utilisateurs pour qu'ils n'aient au moins pas à le parcourir eux-mêmes. Si vous devez le faire de cette façon, je trouve le niveau de sécurité approprié (just-right-kill).
Stockage secret sécurisé
Cela dépend de qui vous essayez de contrecarrer. Si vous empêchez les personnes ayant accès au téléphone de l'utilisateur d'utiliser votre service REST au nom de l'utilisateur, il serait judicieux de trouver une sorte d'API de trousseau de clés sur le système d'exploitation cible et de laisser le SDK (ou l'implémenteur) stocker le clé là-bas. Si ce n'est pas possible, vous pouvez au moins rendre un peu plus difficile l'obtention du secret en le cryptant et en stockant les données cryptées et la clé de cryptage dans des endroits séparés.
Si vous essayez d'empêcher d'autres éditeurs de logiciels d'obtenir votre clé API pour empêcher le développement d'autres clients, seule l'approche chiffrer et stocker séparément fonctionne presque . Il s'agit de crypto whitebox, et à ce jour, personne n'a trouvé de solution vraiment sécurisée aux problèmes de cette classe. Le moins que vous puissiez faire est de toujours émettre une seule clé pour chaque utilisateur afin de pouvoir interdire les clés abusées.
(*) EDIT: les connexions SSL ne doivent plus être considérées comme sécurisées sans prendre des mesures supplémentaires pour les vérifier .
la source
Une API RESTful pure doit utiliser les fonctionnalités standard du protocole sous-jacent:
Pour HTTP, l'API RESTful doit être conforme aux en-têtes standard HTTP existants. L'ajout d'un nouvel en-tête HTTP enfreint les principes REST. Ne réinventez pas la roue, utilisez toutes les fonctionnalités standard des normes HTTP / 1.1 - y compris les codes de réponse d'état, les en-têtes, etc. Les services Web RESTFul doivent exploiter et s'appuyer sur les normes HTTP.
Les services RESTful DOIVENT être SANS STATE. Toute astuce, telle que l'authentification basée sur un jeton qui tente de se souvenir de l'état des demandes REST précédentes sur le serveur, enfreint les principes REST. Encore une fois, c'est un must; autrement dit, si votre serveur Web enregistre des informations relatives au contexte de demande / réponse sur le serveur pour tenter d'établir une session quelconque sur le serveur, alors votre service Web n'est PAS sans état. Et s'il n'est PAS apatride, il n'est PAS RESTFul.
En bout de ligne: à des fins d'authentification / d'autorisation, vous devez utiliser l'en-tête d'autorisation standard HTTP. Autrement dit, vous devez ajouter l'en-tête d'autorisation / d'authentification HTTP dans chaque demande ultérieure qui doit être authentifiée. L'API REST doit suivre les standards du schéma d'authentification HTTP.Les spécificités du formatage de cet en-tête sont définies dans les standards RFC 2616 HTTP 1.1 - section 14.8 Autorisation de la RFC 2616, et dans la RFC 2617 Authentification HTTP: authentification d'accès de base et Digest .
J'ai développé un service RESTful pour l'application Cisco Prime Performance Manager. Recherchez sur Google le document de l'API REST que j'ai écrit pour cette application pour plus de détails sur la conformité de l'API RESTFul ici . Dans cette implémentation, j'ai choisi d'utiliser le schéma d'autorisation HTTP "Basic". - consultez la version 1.5 ou supérieure de ce document de l'API REST et recherchez l'autorisation dans le document.
la source
Dans le Web, un protocole avec état est basé sur le fait d'avoir un jeton temporaire qui est échangé entre un navigateur et un serveur (via un en-tête de cookie ou une réécriture d'URI) à chaque demande. Ce jeton est généralement créé du côté du serveur.Il s'agit d'un élément de données opaque qui a une certaine durée de vie et qui a pour seul but d'identifier un agent utilisateur Web spécifique. Autrement dit, le jeton est temporaire et devient un ÉTAT que le serveur Web doit maintenir au nom d'un agent utilisateur client pendant la durée de cette conversation. Par conséquent, la communication utilisant un jeton de cette manière est STATEFUL. Et si la conversation entre le client et le serveur est STATEFUL, elle n'est pas RESTful.
Le nom d'utilisateur / mot de passe (envoyé sur l'en-tête d'autorisation) est généralement conservé dans la base de données dans le but d'identifier un utilisateur. Parfois, l'utilisateur peut vouloir dire une autre application; cependant, le nom d'utilisateur / mot de passe n'est JAMAIS destiné à identifier un agent utilisateur client Web spécifique. La conversation entre un agent Web et un serveur basée sur l'utilisation du nom d'utilisateur / mot de passe dans l'en-tête Authorization (après l'autorisation HTTP de base) est SANS STATE car le serveur frontal du serveur Web ne crée ni ne gère aucune information STATEque ce soit pour le compte d'un agent utilisateur client Web spécifique. Et sur la base de ma compréhension de REST, le protocole indique clairement que la conversation entre les clients et le serveur doit être SANS STATE. Par conséquent, si nous voulons avoir un vrai service RESTful, nous devons utiliser un nom d'utilisateur / mot de passe (reportez-vous à la RFC mentionnée dans mon article précédent) dans l'en-tête d'autorisation pour chaque appel, PAS un type de jeton de tension (par exemple, des jetons de session créés dans des serveurs Web , Jetons OAuth créés dans les serveurs d'autorisation, etc.).
Je comprends que plusieurs fournisseurs REST appelés utilisent des jetons tels que les jetons d'acceptation OAuth1 ou OAuth2 à transmettre en tant que «Authorization: Bearer» dans les en-têtes HTTP. Cependant, il me semble que l'utilisation de ces jetons pour les services RESTful violerait la véritable signification STATELESS que REST embrasse; car ces jetons sont des données temporaires créées / gérées côté serveur pour identifier un agent utilisateur client Web spécifique pendant la durée valide d'une conversation client / serveur Web. Par conséquent, tout service qui utilise ces jetons OAuth1 / 2 ne doit pas être appelé REST si nous voulons nous en tenir à la signification TRUE d'un protocole STATELESS.
Rubens
la source