web-dev-qa-db-fra.com

Implémentation d'une authentification simple pour PHP REST API

Je travaille sur l'ajout d'une API REST à un ancien site PHP site. Il s'agit de fournir un point de terminaison pour une application interne, donc je suis assez libre dans la façon dont Je conçois des choses et ce que je supporte ou non.

Ce que je dois maintenant ajouter à cette API est un moyen de se connecter, puis d'effectuer des actions en tant qu'utilisateur spécifique. Le site a été construit il y a des années et ne correspond pas nécessairement aux meilleures pratiques de l'époque, donc je suis malheureusement un peu limité dans ma façon de procéder. Tout cela doit être exécuté en PHP 5.4 avec MySQL 5.6.

J'ai lu sur des conceptions courantes pour cela et OAuth1/2 ressemble à la norme la plus courante. Cependant, cela semble être une surpuissance massive pour mes besoins, car il a diverses fonctionnalités dont je n'ai pas besoin et semble très compliqué à mettre en œuvre.

Au lieu de cela, je prévois de faire quelque chose comme ça:

  • Le client appelle un get_session Le point de terminaison API, qui génère un ID de session aléatoire, l'enregistre dans une table de la base de données et le renvoie au client.
  • Le client enregistre cet ID de session.
  • Le client s'authentifie ensuite en envoyant une demande au point de terminaison login, en envoyant le nom d'utilisateur, le mot de passe et l'ID de session (via HTTPS évidemment).
  • Le serveur compare les données à la table utilisateur et, si la connexion est correcte, met à jour la table de session pour associer l'ID de session à l'ID utilisateur correspondant. Cela doit être limité en quelque sorte pour empêcher le forçage brutal.
  • Désormais, le client peut appeler tout autre noeud final en fournissant uniquement son ID de session pour autorisation.
  • À chaque demande, le serveur recherche l'ID de session, voit à quel utilisateur il a été associé et effectue l'action correcte.
  • Le client peut se souvenir de l'ID de session pour une utilisation future, jusqu'à ce qu'il soit supprimé manuellement ou expire après un certain temps.
  • Pour vous déconnecter, le client envoie une demande au point de terminaison logout et le serveur supprime l'association avec le compte d'utilisateur.

Est-ce une conception raisonnable? Ce n'est évidemment pas très sophistiqué, mais je suis à la recherche de quelque chose que je peux implémenter sans tracas énormes ou nécessitant des bibliothèques tierces.

10
Nils

L'un des principaux points de REST en tant que concept est d'éviter l'utilisation de l'état de session afin de faciliter la mise à l'échelle horizontale des ressources de votre point de terminaison REST). Si vous prévoyez d'utiliser le PHP de $_SESSION comme indiqué dans votre question, vous allez vous retrouver dans une position difficile de devoir implémenter le stockage de session partagée dans le cas où vous souhaitez évoluer.

Alors que OAuth serait la méthode préférée pour ce que vous voulez faire, une implémentation complète peut être plus de travail que vous ne voudriez y mettre. Cependant, vous pouvez vous tailler quelque chose d'un demi- mesure et restent sans session. Vous avez probablement déjà vu des solutions similaires auparavant.

  1. Lorsqu'un compte API est provisionné, générez 2 valeurs aléatoires: un jeton et un secret.
  2. Lorsqu'un client fait une demande, il fournit:
    • Le jeton, en texte clair.
    • Une valeur calculée à partir d'une valeur unique, mais connue, et du secret. ex: un HMAC ou une signature cryptographique
  3. Le point de terminaison REST peut alors gérer un magasin de valeurs-clés simple et centralisé de jetons et de secrets, et valider les demandes en calculant la valeur.

De cette façon, vous maintenez l'idéal "sans session" REST) et vous ne transmettez jamais réellement le secret pendant une partie de l'échange.

Exemple client:

$token  = "Bmn0c8rQDJoGTibk";                 // base64_encode(random_bytes(12));
$secret = "yXWczx0LwgKInpMFfgh0gCYCA8EKbOnw"; // base64_encode(random_bytes(24));
$stamp  = "2017-10-12T23:54:50+00:00";        // date("c");
$sig    = hash_hmac('SHA256', $stamp, base64_decode($secret));
// Result: "1f3ff7b1165b36a18dd9d4c32a733b15c22f63f34283df7bd7de65a690cc6f21"

$request->addHeader("X-Auth-Token: $token");
$request->addHeader("X-Auth-Signature: $sig");
$request->addHeader("X-Auth-Timestamp: $stamp");

Exemple de serveur:

$token  = $request->getToken();
$secret = $auth->getSecret($token);
$sig    = $request->getSignature();

$success = $auth->validateSignature($sig, $secret);

Il convient de noter que si vous décidez d'utiliser un horodatage en tant que nonce, vous ne devez accepter que les horodatages générés au cours des dernières minutes pour éviter les attaques de relecture. La plupart des autres schémas d'authentification incluront des composants supplémentaires dans les données signées, tels que le chemin d'accès aux ressources, des sous-ensembles de données d'en-tête, etc. pour verrouiller davantage la signature afin de ne l'appliquer qu'à une seule demande.

20
Sammitch

Je dirais que vous devez générer un jeton unique et l'utiliser pour la communication. Fondamentalement:

  • Le client envoie le nom d'utilisateur/mot de passe à la ressource login.
  • Le serveur vérifie la combinaison nom d'utilisateur/mot de passe. S'il est correct, il génère un toquen unique, l'enregistre dans la table sessions et le renvoie à l'utilisateur, avec une mise à jour d'état comme logged_in = TRUE.
  • Désormais, toute autre demande envoyée par l'utilisateur doit inclure un champ token (soit sous la forme d'un champ POST ou d'un paramètre GET). À ce stade, je reconsidérerais l'utilisation de REST et n'utiliserais que POST requêtes pour tout, avec le operation comme un POST champ. Cela n'ajouterait pas le jeton à l'URL et, donc, le laisserait être enregistré sur un historique de navigation Web, des routeurs et autres.
  • À chaque demande, le serveur doit vérifier si le jeton existe et s'il est valide. Sinon, renvoyez simplement un message d'erreur comme 403 Forbidden Et logged_in = FALSE.

Le système pourrait également nécessiter d'envoyer d'autres données pour les rendre plus sécurisées, comme un unique id Généré par le client et des trucs comme ça, qui devraient être envoyés avec le jeton et vérifié côté serveur.

1
Alejandro Iván

Les points de votre plan sont essentiellement les fonctionnalités de base d'OAuth. Cela dépend de vos besoins. Si vos API sont à usage interne uniquement, vous pouvez envoyer une clé secrète avec l'authentification HMAC-SHA.

0
Fritz M. Neumann

Eh bien, vous avez beaucoup écrit comment inventer des cookies à partir de zéro et comment les stocker dans la base de données.

Dans le même temps, vous avez déjà des noms d'utilisateur, des mots de passe et HTTPS pour le transfert de données de sécurité. Pourquoi n'utilisez-vous pas uniquement des cookies?

0
user1597430