NOTE: J'avais 4 primes pour cette question, mais aucune des réponses votées ci-dessous n'est la réponse nécessaire pour cette question. Tout ce dont vous avez besoin est dans la mise à jour 3 ci-dessous, il suffit de rechercher le code Laravel à implémenter.
MISE À JOUR 3: Cet organigramme est exactement le flux que j'essaie d'accomplir, tout ce qui suit est la question initiale avec certaines mises à jour plus anciennes. Cet organigramme résume tout ce qui est nécessaire.
Les parties vertes dans l’organigramme ci-dessous sont les parties que je sais faire. Les parties rouges accompagnées de leurs notes sont ce que je recherche pour obtenir de l’aide en utilisant le code Laravel.
J'ai fait beaucoup de recherches, mais les informations étaient toujours courtes et incomplètes en ce qui concerne l'utilisation de Laravel avec un cookie JWT httponly pour une API auto-consommatrice (la plupart des tutoriels en ligne ne montrent que JWT). Il semble qu’un cookie contenant un JWT de Passport devrait être utilisé pour identifier l’utilisateur du côté Javascript lorsqu’il est envoyé avec chaque demande adressée au serveur afin de valider que l’utilisateur est bien ce qu’ils disent. sont.
Il y a aussi quelques éléments supplémentaires nécessaires pour avoir une idée complète de la façon de faire fonctionner cette configuration, que je n'ai pas rencontrés dans un seul tutoriel qui couvre ceci:
J'espère qu'une réponse à cette question constituera un guide facile à suivre pour les futurs lecteurs et ceux qui luttent actuellement pour trouver une réponse couvrant les points ci-dessus sur une API auto-consommatrice.
PDATE 1:
CreateFreshApiToken
, mais cela n'a pas fonctionné pour révoquer les jetons de l'utilisateur (pour les points 3 et 4 ci-dessus). Ceci est basé sur ce commentaire par un noyau laravel, en parlant du middleware CreateFreshApiToken
:Les jetons JWT créés par ce middleware ne sont stockés nulle part. Ils ne peuvent pas être révoqués ou "ne pas exister". Ils fournissent simplement un moyen d'autoriser vos appels d'api par le cookie laravel_token. Ce n'est pas lié aux jetons d'accès. En outre, vous n'utiliseriez normalement pas de jetons émis par les clients sur la même application qui les émet. Vous les utiliseriez dans une application tierce ou tierce. Utilisez le middleware ou les jetons émis par le client, mais pas les deux en même temps.
Donc, il semble être en mesure de prendre en compte les points 3 et 4 pour révoquer les jetons. Ce n'est pas possible si vous utilisez le middleware CreateFreshApiToken
.
Authorization: Bearer <token>
n’est pas la voie à suivre s’agissant du cookie sécurisé httpOnly. Je pense que la requête/réponse est supposée inclure le cookie sécurisé httpOnly comme en-tête de requête/réponse, comme ceci, basé sur le laravel docs:Lorsque vous utilisez cette méthode d’authentification, l’échafaudage JavaScript Laravel par défaut) indique à Axios de toujours envoyer les en-têtes X-CSRF-TOKEN et X-Requested-With.
headerswindow.axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': (csrf_token goes here)
};
C'est aussi la raison pour laquelle je cherche une solution qui couvre tous les points ci-dessus. Toutes mes excuses, j'utilise Laravel 5.6 pas 5.5.
PDATE 2:
Il semble que la combinaison Accord de mot de passe/Actualiser l’attribution de jetons est la voie à suivre. Vous recherchez un guide d'implémentation facile à suivre à l'aide de la combinaison Accord de mot de passe/Actualiser l'attribution de jeton.
Mot de passe Subvention: Cette subvention convient aux clients en qui nous avons confiance, comme une application mobile pour notre propre site Web. Dans ce cas, le client envoie les informations d'identification de connexion de l'utilisateur au serveur d'autorisation, lequel émet directement le jeton d'accès.
Refresh Token Grant: Lorsque le serveur émet un jeton d'accès, il définit également une date d'expiration pour le jeton d'accès. L'attribution de jeton d'actualisation est utilisée lorsque nous souhaitons actualiser le jeton d'accès après son expiration. Dans ce cas, le serveur d'autorisation envoie un jeton d'actualisation lors de l'émission du jeton d'accès, qui peut être utilisé pour demander un nouveau jeton d'accès.
Je cherche une réponse facile à mettre en œuvre, simple et globale à l’aide du combo Accorder le mot de passe/actualiser les jetons qui couvre toutes les parties des 5 points originaux ci-dessus avec le cookie httpOnly secure, créant/révoquant/jetons d'actualisation, création de cookies de connexion, révocation de cookies de déconnexion, méthodes de contrôleur, CSRF, etc.
Laravel Passport JWT
Pour utiliser cette fonctionnalité, vous devez désactiver la sérialisation des cookies. Laravel 5.5 présente un problème de sérialisation/non sérialisation des valeurs de cookies. Vous pouvez en savoir plus à ce sujet ici ( https://laravel.com/docs/5.5/upgrade )
Sois sûr que
vous avez <meta name="csrf-token" content="{{ csrf_token() }}">
dans la tête du modèle de votre lame
axios est configuré pour utiliser csrf_token à chaque requête.
Vous devriez avoir quelque chose comme ça dans resources/assets/js/bootstrap.js
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
Les parties importantes sont:
Laravel\Passport\HasApiTokens
à votre modèle User
driver
de la garde api
d'authentification sur passport
dans votre config/auth.php
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
à votre groupe de middleware web
dans app/Http/Kernel.php
Notez que vous pouvez probablement ignorer les migrations et la création de clients.
/login
En transmettant vos informations d'identification. Vous pouvez faire une demande AJAX ou un formulaire normal à envoyer.Si la demande de connexion est AJAX (en utilisant axios), les données de réponse seront au format HTML, mais ce qui vous intéresse est le code de statut.
axios.get(
'/login,
{
email: '[email protected]',
password: 'secret',
},
{
headers: {
'Accept': 'application/json', // set this header to get json validation errors.
},
},
).then(response => {
if (response.status === 200) {
// the cookie was set in browser
// the response.data will be HTML string but I don't think you are interested in that
}
// do something in this case
}).catch(error => {
if (error.response.status === 422) {
// error.response.data is an object containing validation errors
}
// do something in this case
});
Lors de la connexion, le serveur trouve l'utilisateur à l'aide des informations d'identification fournies, génère un jeton basé sur les informations de l'utilisateur (identifiant, email ...) (ce jeton n'est enregistré nulle part), puis le serveur renvoie une réponse avec un cookie crypté contenant le jeton généré. .
En supposant que vous avez un itinéraire protégé
Route::get('protected', 'SomeController@protected')->middleware('auth:api');
Vous pouvez faire un appel ajax en utilisant axios comme d’habitude. Les cookies sont automatiquement configurés.
axios.get('/api/protected')
.then(response => {
// do something with the response
}).catch(error => {
// do something with this case of error
});
Lorsque le serveur reçoit l'appel, il déchiffre la demande laravel_cookie
Et obtient les informations de l'utilisateur (ex: id, email ...). Ensuite, avec cette information, une recherche dans la base de données permet de vérifier si l'utilisateur existe. Si l'utilisateur est trouvé, il est autorisé à accéder à la ressource demandée. Sinon un 401 est retourné.
Invalidation du jeton JWT. Comme vous mentionnez le commentaire, vous n'avez pas à vous inquiéter car ce jeton n'est enregistré nulle part sur le serveur.
Concernant le point 3 Laravel 5.6 Auth a une nouvelle méthode logoutOtherDevices
. Vous pouvez en apprendre plus à partir de cet emplacement ( https://laracasts.com/series/whats-new -in-laravel-5-6/episodes/7 ) car la documentation est très légère.
Si vous ne parvenez pas à mettre à jour votre version de Laravel), vous pouvez vérifier comment elle est effectuée dans la version 5.6 et construire votre propre implémentation pour la version 5.5.
Point 4 de votre question. Jetez un coup d'œil aux contrôleurs trouvés dans app/Http/Controllers/Auth
.
En ce qui concerne access_tokens et refresh_tokens, il s'agit d'une approche totalement différente et plus complexe. Vous pouvez trouver de nombreux tutoriels en ligne expliquant comment le faire.
J'espère que ça aide.
PS Bonne année!! :)
J'ai également implémenté Laravel passeport dans mon projet et je pense avoir couvert la plupart des points que vous avez mentionnés dans votre question.
Authorization: Bearer <token>
).Faites-moi savoir si vous êtes clair avec les points ci-dessus.
Plus d'informations que vous pouvez voir ici
http://esbenp.github.io/2017/03/19/modern-rest-api-laravel-part-4/
J'essaierai d'y répondre de manière générique afin que la réponse soit applicable à travers les frameworks, les implémentations et les langages car les réponses à toutes les questions peuvent être dérivées des spécifications générales du protocole ou de l'algorithme.
C'est la première chose à décider. En ce qui concerne SPA, les deux options possibles sont:
Les raisons pour lesquelles je ne mentionne pas le type de subvention implicite en option sont les suivantes:
(Le type d'octroi d'informations d'identification du client est exclu de la présente discussion, car il est utilisé lorsque le client n'agit pas au nom d'un utilisateur. Par exemple, un travail par lots)
En cas de type d'autorisation Code d'autorisation, le serveur d'autorisation est généralement un serveur différent du serveur de ressources. Il est préférable de séparer le serveur d'autorisation et de l'utiliser comme serveur d'autorisation commun pour tous les SPA de l'entreprise. C'est toujours la solution recommandée.
Ici (dans le type d’octroi de code d’autorisation), le flux ressemble à celui ci-dessous:
Cache-Control: no-cache, no-store
, Pragma: no-cache
, Expires: 0
Par ailleurs, pour le type d’octroi d’autorisation, avec le mot de passe du propriétaire de la ressource, le serveur d’autorisation et le serveur de ressources sont identiques. Il est plus facile à mettre en œuvre et peut également être utilisé si cela convient aux exigences et aux délais de mise en œuvre.
Reportez-vous également à ma réponse à ce sujet ici pour plus de détails sur le type de subvention du propriétaire de la ressource.
Il peut être important de noter ici que dans un SPA, toutes les routes protégées ne doivent être activées qu'après avoir appelé un service approprié pour garantir la présence de jetons valides dans la demande. De même, les API protégées doivent également disposer de filtres appropriés pour valider les jetons d'accès.
De nombreux SPA stockent l'accès et/ou actualisent le jeton dans le stockage local ou le stockage de session du navigateur. La raison pour laquelle je pense que nous ne devrions pas stocker les jetons dans ces stockages de navigateur est la suivante:
Si XSS se produit, le script malveillant peut facilement lire les jetons à partir de là et les envoyer à un serveur distant. Le serveur distant ou l'attaquant n'aura aucun problème à emprunter l'identité de l'utilisateur victime.
le stockage local et le stockage de session ne sont pas partagés entre les sous-domaines. Ainsi, si deux SPA fonctionnent sur des sous-domaines différents, nous n'obtiendrons pas la fonctionnalité SSO car le jeton stocké par une application ne sera pas disponible pour l'autre application de l'organisation.
Si, toutefois, les jetons sont toujours stockés dans l'un de ces stockages de navigateur, une empreinte digitale appropriée doit être incluse. L'empreinte digitale est une chaîne d'octets aléatoire très forte sur le plan cryptographique. La chaîne Base64 de la chaîne brute sera alors stockée dans un cookie HttpOnly
, Secure
, SameSite
avec le préfixe de nom __Secure-
. Valeurs propres pour les attributs Domain
et Path
. Un hachage SHA256 de la chaîne sera également passé dans une revendication de JWT. Ainsi, même si une attaque XSS envoie le jeton d'accès JWT à un serveur distant contrôlé par un attaquant, il ne peut pas envoyer la chaîne d'origine dans le cookie et le serveur peut donc rejeter la demande en raison de l'absence du cookie. En outre, l'injection de script et XSS peut être davantage atténuée en utilisant un content-security-policy
en-tête de réponse.
Remarque:
SameSite=strict
s'assure que le cookie donné n'accompagnera pas les demandes provenant d'un autre site (AJAX ou via l'hyperlien suivant). En termes simples, toute requête provenant d’un site ayant le même "domaine enregistrable" que le site cible sera autorisée. Par exemple. Si " http://www.example.com " est le nom du site, le domaine enregistrable est "example.com". Pour plus de détails, voir référence no. 3 dans la dernière section ci-dessous. Ainsi, il offre une certaine protection contre le CSRF. Toutefois, cela signifie également que si l'URL fournie est un forum, un utilisateur authentifié ne peut pas suivre le lien. S'il s'agit d'une restriction sérieuse pour une application, SameSite=lax
_ peut être utilisé, ce qui autorisera les requêtes intersites tant que les méthodes HTTP sont sécurisées, à savoir. GET, HEAD, OPTIONS et TRACE. Comme CSRF est basé sur des méthodes peu sûres comme POST, PUT, DELETE, lax
fournit toujours une protection contre CSRF
Pour permettre à un cookie d'être transmis dans toutes les demandes à n'importe quel sous-domaine de "exemple.com", l'attribut de domaine du cookie doit être défini sur "exemple.com".
secure
et httpOnly
. Ainsi, si XSS se produit, le script malveillant ne peut pas lire et les envoyer au serveur distant. XSS peut toujours emprunter l'identité de l'utilisateur à partir du navigateur de l'utilisateur, mais si le navigateur est fermé, le script ne peut plus causer de dommages. L'indicateur secure
garantit que les jetons ne peuvent pas être envoyés via des connexions non sécurisées - SSL/TLS est obligatoiredomain=example.com
, par exemple, garantit que le cookie est accessible à travers tous les sous-domaines. Ainsi, différentes applications et serveurs au sein de l'entreprise peuvent utiliser les mêmes jetons. La connexion est requise une seule foisLes jetons sont généralement des jetons JWT. Généralement, le contenu du jeton n'est pas secret. Par conséquent, ils ne sont généralement pas cryptés. Si le chiffrement est requis (peut-être parce que certaines informations confidentielles sont également transmises dans le jeton), il existe une spécification distincte JWE. Même si le cryptage n'est pas requis, nous devons garantir l'intégrité des jetons. Personne (utilisateur ou attaquant) ne devrait être en mesure de modifier les jetons. S'ils le font, le serveur devrait pouvoir détecter cela et refuser toutes les demandes avec les jetons falsifiés. Pour garantir cette intégrité, les jetons JWT sont signés numériquement à l'aide d'un algorithme tel que HmacSHA256. Afin de générer cette signature, une clé secrète est requise. Le serveur d'autorisation possédera et protégera le secret. Chaque fois que l'API du serveur d'autorisation est appelée pour valider un jeton, le serveur d'autorisation recalculera le HMAC sur le jeton transmis. Si cela ne correspond pas à l'entrée HMAC, cela donne une réponse négative. Les jetons JWT sont renvoyés ou stockés dans un format codé Base64.
Toutefois, pour chaque appel d'API sur le serveur de ressources, le serveur d'autorisations n'est pas impliqué dans la validation du jeton. Le serveur de ressources peut mettre en cache les jetons émis par le serveur d'autorisation. Le serveur de ressources peut utiliser une grille de données en mémoire (à savoir. Redis) ou, si tout ne peut pas être stocké dans la RAM, une base de données basée sur LSM (à savoir Riak avec Level DB) pour stocker les jetons.
Pour chaque appel d'API, le serveur de ressources vérifierait son cache.
Si le jeton d'accès n'est pas présent dans la mémoire cache, les API doivent renvoyer un message de réponse approprié et un code de réponse 401 de sorte que le SPA puisse rediriger l'utilisateur vers une page appropriée où il serait demandé à l'utilisateur de se reconnecter.
Si le jeton d'accès est valide mais a expiré (remarque, les jetons JWT contiennent entre autres le nom d'utilisateur et la date d'expiration), les API doivent renvoyer un message de réponse approprié et un code de réponse 401 de sorte que le SPA puisse invoquer une API de serveur de ressources appropriée. renouveler le jeton d'accès avec le jeton d'actualisation (avec les en-têtes de cache appropriés). Le serveur invoquerait ensuite le serveur d'autorisation avec le jeton d'accès, le jeton d'actualisation et le secret du client, et le serveur d'autorisation pourrait renvoyer le nouvel accès et les jetons d'actualisation qui se retrouveraient éventuellement dans le SPA (avec les en-têtes de cache appropriés). Ensuite, le client doit réessayer la demande d'origine. Tout cela sera géré par le système sans intervention de l'utilisateur. Un cookie distinct pourrait être créé pour stocker le jeton d'actualisation, similaire au jeton d'accès, mais avec la valeur appropriée pour l'attribut Path
, de sorte que le jeton d'actualisation n'accompagne pas toutes les demandes, mais qu'il ne soit disponible que dans les demandes de renouvellement.
Si le jeton d'actualisation n'est pas valide ou a expiré, les API doivent renvoyer un message de réponse et un code de réponse 401 appropriés, de sorte que le SPA puisse rediriger l'utilisateur vers une page appropriée sur laquelle il serait demandé à l'utilisateur de se reconnecter.
Les jetons d'accès ont généralement une courte période de validité, par exemple 30 minutes. Le jeton d'actualisation a généralement une période de validité plus longue, soit 6 mois. Si le jeton d'accès est compromis, l'attaquant peut emprunter l'identité de l'utilisateur victime uniquement tant que le jeton d'accès est valide. L'attaquant ne disposant pas du secret client, il ne peut pas demander au serveur d'autorisation un nouveau jeton d'accès. L'attaquant peut toutefois demander le renouvellement du jeton au serveur de ressources (comme dans la configuration ci-dessus, la demande de renouvellement passe par le serveur de ressources pour éviter de stocker le secret du client dans un navigateur), mais étant donné les autres étapes effectuées, il est peu probable et le serveur peut prendre des mesures de protection supplémentaires en fonction de l'adresse IP.
Si cette courte période de validité du jeton d'accès aide le serveur d'autorisation à révoquer les jetons émis des clients, si nécessaire. Le serveur d'autorisation peut également conserver un cache des jetons émis. Les administrateurs du système peuvent ensuite, si nécessaire, marquer les jetons de certains utilisateurs comme révoqués. À l'expiration du jeton d'accès, lorsque le serveur de ressources accédera au serveur d'autorisations, l'utilisateur sera obligé de se connecter à nouveau.
Afin de protéger l'utilisateur de CSRF, nous pouvons suivre l'approche suivie dans des cadres tels que Angular (comme expliqué dans la Angular HttpClient documentation) où le serveur doit envoyer un cookie autre que HttpOnly (en d’autres termes, un cookie lisible) contenant une valeur imprévisible unique pour cette session particulière. Il s’agit d’une valeur aléatoire forte sur le plan cryptographique. Le client lira alors toujours le cookie et l’enverra. la valeur dans un en-tête HTTP personnalisé (sauf GET & HEAD qui ne sont supposées avoir aucune logique de changement d'état. Remarque: CSRF ne peut rien lire dans l'application Web cible en raison de la même politique d'origine) que le serveur puisse vérifier la valeur de l'en-tête et du cookie, les formulaires inter-domaines ne pouvant ni lire le cookie ni définir d'en-tête personnalisé, dans le cas de demandes CSRF, la valeur de l'en-tête personnalisé sera manquante et le serveur pourra détecter l'attaque
Pour protéger l’application de la connexion CSRF, vérifiez toujours l’en-tête referer
et acceptez les demandes uniquement lorsque referer
est un domaine approuvé. Si l'entête referer
est absente ou s'il s'agit d'un domaine ne figurant pas sur la liste blanche, rejetez simplement la demande. Lorsque vous utilisez SSL/TLS, referrer
est généralement présent. Pages de renvoi (principalement informatives et ne contenant ni formulaire de connexion ni contenu sécurisé) peuvent être un peu détendues et autoriser les requêtes avec en-tête manquant referer
TRACE
La méthode HTTP doit être bloquée sur le serveur car elle peut être utilisée pour lire le cookie httpOnly
.
Définissez également l'en-tête Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
Pour n'autoriser que les connexions sécurisées afin d'empêcher toute intervention de man-in-the-middle d'écraser les cookies CSRF d'un sous-domaine
De plus, le paramètre SameSite
mentionné ci-dessus devrait être utilisé
Enfin, SSL/TLS est obligatoire pour toutes les communications. Les versions TLS inférieures à 1.1 ne sont pas acceptables pour la conformité PCI/DSS. Des suites de chiffrement appropriées doivent être utilisées pour assurer la confidentialité du transfert et le chiffrement authentifié. En outre, les jetons d'accès et d'actualisation doivent être mis sur liste noire dès que l'utilisateur clique explicitement sur "Déconnexion" pour éviter toute possibilité d'utilisation abusive de jetons.