Je cherche à améliorer la sécurité d'une API REST accessible via SSL. Le service Web est multi-locataire, de sorte que chaque locataire se voit attribuer un TenantId.
Le problème auquel je suis confronté peut se résumer comme suit:
Actuellement
Actuellement, nous émettons manuellement (c'est-à-dire hors processus - par téléphone, etc.) une clé API pour chaque client, qu'ils incluent comme en-tête HTTP dans chaque demande. Cette clé API correspond à un TenantId. Lorsque le client envoie une demande de connexion à l'API REST, nous déterminons ensuite le TenantId, qui à son tour nous permet de vérifier le nom d'utilisateur/mot de passe dans la base de données des locataires correcte. Une fois que cela réussit, nous émettons un cookie HTTP limité dans le temps. Ce cookie est ensuite utilisé sur les REST requêtes suivantes. En interne, ce cookie est lié à une session et la session contient des informations de profil utilisateur pour réduire la charge de base de données.
Nous sommes conscients que cela comporte des risques de sécurité inhérents. La clé API peut être facilement extraite du code source client désassemblé. Nous avons également l'intention de construire notre propre client en tant que SPA en JavaScript, lisible par tous. Bien que nous puissions révoquer/modifier des clés, je recherche une implémentation standardisée plus sécurisée.
Alternative
Si je comprends bien, je pourrais utiliser une alternative basée sur les jetons aux cookies. HMAC est un mécanisme d'authentification commun pour les applications Web, mais cela nécessite le stockage d'un secret partagé sur le client et le serveur. Ce secret semble avoir le même problème que la clé API ci-dessus; en ce qu 'il peut fuir.
J'ai également lu un peu sur JWT, qui semble étendre le concept HMAC en ce que le serveur peut conserver les données de "session" de l'utilisateur dans le jeton, ce qui réduit le nombre d'appels de base de données pour les informations utilisateur/profil . Le jeton JWT est utilisé à la suite d'une connexion avec nom d'utilisateur/mot de passe réussie. Il n'y a donc pas de problème de secret partagé ici, n'est-ce pas?
J'ai également lu un peu sur OAuth2, en particulier le Grant Owner Password Credentials Grant . Je ne sais même pas si OAuth2 résout mon problème.
Détermination du locataire
La première étape consiste à déterminer le locataire. Plutôt que d'utiliser la clé API pour mapper à un TenantId, je pourrais soit:
Validation de l'appelant de l'application
Deuxièmement, je dois déterminer si une application cliente donnée est autorisée à accéder à l'API REST. Sur cette partie, je suis bel et bien à court d'idées brillantes. Est-ce là que OAuth2 arrive à la porter secours?
La seule façon dont je peux penser pour résoudre ce problème est d'émettre l'application appelante avec une clé API expirant temporairement après que l'utilisateur s'est connecté. Bien que cela ne garantisse pas que l'application n'est pas voyou, cela réduit le risque pour un attaquant qui a également eu accès aux informations d'identification d'un utilisateur.
Ma solution de cookies HTTP actuelle est-elle acceptable? Dois-je plutôt passer à un mécanisme d'authentification basé sur des jetons?
Je me réjouis de tout conseil que vous pourriez avoir. Je me promène dans des masses d'informations et j'essaie de comprendre tout cela. Toute orientation serait heureusement reçue!
Vous devriez regarder oauth 2.0 (RFC 6749), il a un certain nombre de flux qui pourraient répondre à vos besoins.
Tout d'abord, vous devez savoir si les clients sont publics ou confidentiels. L'émission de clés API clientes pendant que vous le faites est inutile si les clients sont des navigateurs Web ou des applications téléchargés sur des appareils mobiles car le secret ne peut pas être conservé. S'il s'agit de serveurs, les clés API sont bonnes. C'est donc votre première considération. La section 2.1 du rfc traite des types de clients et déterminera les flux applicables.
Vous devez séparer l'identification du service client de l'utilisateur pour lequel le client agit. Votre base de données principale doit avoir une association d'utilisateurs avec les locataires et éventuellement des clés API avec les utilisateurs ou utilisateur + locataire si vous souhaitez étendre à une seule location. J'ai mis en place des services où les utilisateurs peuvent participer à plusieurs locataires afin qu'ils s'appliquent ou non à votre cas. J'ai utilisé avec succès la demande d'autorisation d'accès au mot de passe du propriétaire de la ressource afin qu'un utilisateur puisse fournir son nom d'utilisateur et son mot de passe à partir d'un navigateur Web. Il semble que vous ayez utilisé une méthode hors bande pour autoriser le client afin que les informations d'identification de l'utilisateur ne soient pas requises. Mais si le client est un navigateur Web ou une application mobile, vous ne pouvez pas vous fier à l'authentification du client et devez obtenir l'autorisation de l'utilisateur, ce qui signifie fournir des informations d'identification utilisateur.
Quel que soit le flux que vous utilisez, vous obtenez une réponse comme suit:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
Le jeton d'accès retourné est ce que le client utilise dans ses demandes afin qu'il puisse prouver au serveur que ses demandes sont autorisées.
Vous avez deux options pour l'en-tête d'autorisation, "porteur" ou "mac".
Authorization: Bearer xxxxxx
Si vos clients utilisent TLS, les jetons au porteur sont la voie à suivre simple. Voir la section 7.1 de la RFC pour plus de détails.
Enfin, puisque les jetons d'accès ne sont que temporaires, vos clients peuvent utiliser le jeton d'actualisation pour demander un nouveau jeton d'accès une fois que le jeton d'accès approche ou dépasse son expiration. C'est à vous de décider si vous souhaitez utiliser des jetons d'actualisation ou non. Les clients devront les stocker en toute sécurité au moins le même niveau de protection qu'ils utilisent pour leurs clés API. Dans votre système, les jetons d'actualisation peuvent être inutiles pour les interactions serveur-serveur si la durée de vie de l'accès est suffisante et que l'authentification des utilisateurs n'est pas requise. Si l'authentification de l'utilisateur est requise, les jetons d'actualisation peuvent être utiles si le stockage client fournit un niveau de protection suffisant.
Je pense que ce que vous avez en ce moment est très bien en ce qui concerne la façon dont vous identifiez un locataire - chaque client aura besoin de certains sorte d'identifiant, une clé API est aussi bonne que n'importe laquelle. Étant donné que tout fonctionne sur SSL, vous n'avez pas vraiment à vous soucier de son exposition (au moins pendant le transit).
Vous semblez vous préoccuper de côté client stockage de la clé API, cependant, ce n'est finalement pas votre travail. Il est de la responsabilité du client de garder ses informations privées en sécurité (ce n'est pas le travail de votre banque de garder votre PIN sûr). Votre travail consiste à protéger le serveur et vos clients contre toute utilisation abusive. One moyen de le faire est de pouvoir révoquer/bloquer une clé API et/ou une session.
L'argument cookie vs HMAC peut ne pas être aussi important que vous le pensez. Demandez-vous ce qui change si vous passez d'un cookie à un jeton? Un HMAC est certainement plus sécurisé étant donné qu'il est signé, cependant, vous pouvez tout aussi bien crypter le contenu du cookie. En fin de compte, cela vous donnera toujours le même mal de tête de stockage que le cookie. La partie importante sur laquelle se concentrer est de savoir combien de dégâts une personne pourrait-elle faire si elle réussissait à y accéder? Cela vous amènera à réfléchir à A. la sécurité du cookie/jeton et B. comment vous pouvez protéger le serveur/client s'il devait tomber entre des mains malveillantes.
Si vous devez également gérer le client autorisation alors vous devriez probablement regarder OAuth, étant donné que c'est précisément pour cela qu'il est conçu.
une alternative est d'utiliser des clés publiques-privées.
l'application cliente (par exemple angularjs) aura la clé publique du serveur (par exemple, web api)
en utilisant la clé publique du serveur, le client doit crypter les informations d'identification de l'utilisateur (par exemple, nom d'utilisateur, mot de passe haché, etc.). le jeton généré sera envoyé au serveur; soit par en-têtes ou par requête
le serveur déchiffrera alors le jeton, obtiendra le nom d'utilisateur et le mot de passe haché. puis récupérez le nom d'utilisateur persistant + le mot de passe haché dans la base de données. l'authentification consiste à vérifier si les deux mots de passe hachés correspondent (un du jeton et un de la base de données).
dans cette alternative, le mot de passe de l'utilisateur n'est pas envoyé en texte brut. il est chiffré/haché avant d'être envoyé. le client peut soit stocker ce jeton dans le stockage local (navigateur), soit générer un nouveau jeton à chaque fois qu'il appelle le serveur.