web-dev-qa-db-fra.com

Sécurisation des mots de passe pour REST Authentification

Je suis en train de développer une REST utilisant le Spring Framework, dans le cadre des exigences, nous devons sécuriser les différentes fonctions du système à différents rôles d'utilisateur (trucs assez standard). La méthode de détermination des rôles pour l'utilisateur actuellement connecté est que chaque fois qu'il appelle une URL REST depuis le frontend, j'ajoute une chaîne codée Base 64 à l'en-tête de la demande. Cette chaîne lorsqu'elle est décodée résout leur nom d'utilisateur et un hachage de mot de passe généré par bCrypt dans ce format nom d'utilisateur: hashedpassword.

Je suis légèrement préoccupé par le fait que ce n'est pas sécurisé, même si la demande sera effectuée via une connexion HTTP sécurisée, car cela pourrait donner à un pirate potentiel un accès au moins au nom d'utilisateur des utilisateurs. Ils n'ont pas pu obtenir le mot de passe car il s'agit simplement d'une valeur hachée, mais ils peuvent utiliser cette valeur hachée pour appeler avec succès l'API REST.

Comment puis-je sécuriser ce système correctement? Dois-je ajouter un jeton de session ou une sorte de clé générée aléatoirement pour la session?

Ma question de suivi est alors comment puis-je faire cela RESTfully? Je pensais que je pouvais générer (en utilisant bCrypt) un hachage qui représentait le nom d'utilisateur: hashedpassword ensemble lors de la connexion, l'enregistrer dans la base de données et vérifier cela chaque fois qu'un appel REST était effectué. l'utilisateur se déconnecte, il suffit de le mettre à null. Rinçage et répétition. De cette façon, tout attaquant potentiel n'obtiendrait qu'une seule chaîne bCrypt qui n'exposerait pas le nom d'utilisateur, mais il pourrait toujours utiliser cette chaîne pour appeler le REST API.

6
JamesENL

Les liens suivants peuvent vous fournir une réponse approfondie:

Veuillez garder à l'esprit qu'il est préférable de ne pas utiliser la combinaison nom d'utilisateur-mot de passe dans chaque demande que vous faites. Mieux vaut authentifier l'utilisateur, générer un token côté serveur, le communiquer au client (par exemple dans un cookie) et utiliser ce token comme authentification pour les requêtes suivantes. Ce lien peut vous guider dans ce processus: https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Session_Management_Cheat_Sheet.md .

5
Michael

Si vous avez seulement besoin de fournir une authentification mais que les données transmises sur le câble ne sont pas sensibles, il existe un meilleur moyen sans la surcharge/latence de SSL.

Supposons que vous disposez d'une application client mobile; obligez d'abord l'utilisateur à s'inscrire ou à s'inscrire en fournissant son e-mail (nom d'utilisateur) et son mot de passe dans un formulaire Web distinct (ne faisant pas partie de l'application ou du service REST). Ensuite, une fois l'inscription réussie, vous répondez avec un clé d'utilisateur (il peut s'agir d'une clé secrète partagée stockée dans le compte d'utilisateur sur le serveur (base de données) pour le chiffrement symétrique ou d'une clé publique pour le chiffrement asymétrique où la clé privée correspondante est stockée dans le compte d'utilisateur sur le serveur (base de données). tout se fait à l'aide d'un formulaire Web sur SSL.

Désormais, lorsque l'utilisateur ouvre l'application cliente, vous devez lui demander ses informations d'identification qui seront envoyées avec chaque demande au service RESTful. Ils doivent fournir leur nom, mot de passe et clé de cryptage qu'ils ont reçus précédemment. Cela ne doit être fait qu'une seule fois. L'application fournit ensuite un en-tête http avec chaque demande qui ressemble à ceci:

AUTHENTIFIER> nom d'utilisateur: horodatage: chiffré {mot de passe: horodatage}/AUTHENTIFIER>

Notez que le mot de passe et l'horodatage à l'intérieur du {} sont cryptés à l'aide de la clé de l'utilisateur. L'horodatage est mis à jour à chaque demande.

Implémentez un filtre d'authentification sur le serveur qui effectue les opérations suivantes:

Vérifiez d'abord l'horodatage et, s'il est expiré (disons plus d'une seconde), envoyez un code de réponse HTTP NON AUTORISÉ. Si l'horodatage est valide, recherchez le nom d'utilisateur dans la base de données de votre compte d'utilisateur. S'il n'est pas trouvé, envoyez une réponse HTTP NON AUTORISÉE. Si le nom d'utilisateur est trouvé, récupérez la clé de chiffrement stockée pour cet utilisateur (n'oubliez pas qu'il peut s'agir d'une clé secrète partagée ou de la clé privée de la clé publique de l'utilisateur). Déchiffrez le {mot de passe: horodatage} chiffré. Le mot de passe déchiffré doit correspondre au mot de passe des utilisateurs stocké dans votre base de données (le mot de passe lui-même peut également être chiffré dans la base de données à l'aide d'une autre clé pour plus de sécurité) et l'horodatage déchiffré doit également correspondre à l'horodatage non chiffré envoyé dans l'en-tête AUTHENTICATE ci-dessus. Sinon, envoyez un code de réponse HTTP NON AUTORISÉ. En cas de succès, la demande a été authentifiée sans l'utilisation de cookies/sessions.

Vous pouvez également mettre en cache les détails de l'utilisateur pour éviter d'effectuer une recherche de base de données à chaque demande.

Maintenant, si quelqu'un espionne et intercepte la demande, il ne pourra pas la réutiliser pour y accéder car soit l'horodatage sera invalide, soit s'il met à jour l'horodatage non chiffré pour être valide, il ne correspondra pas à l'horodatage chiffré (après le filtre d'authentification le déchiffre).

Un autre avantage de cette approche par rapport à l'utilisation d'une seule clé d'application est que vous avez maintenant un contrôle total sur qui peut accéder à votre service en mettant une date d'expiration sur le compte d'utilisateur dans la base de données (en mettant effectivement en œuvre un service basé sur un abonnement). C'est génial car au début, vous voudrez peut-être obtenir autant d'utilisateurs que possible avec un abonnement d'essai (gratuit pendant 1 an, par exemple), puis bloquer l'accès à cet utilisateur plus tard s'il n'a pas payé pour prolonger la date d'expiration du compte :)

0
user3598746