Sécurisation et Authentification des APIs avec JWT et LexikJWT

11 min de lecture

1. Introduction à l'authentification avec JWT

1.1 Pourquoi utiliser les tokens JWT

L'authentification est un élément crucial dans la sécurité des applications. Elle permet de vérifier l'identité de l'utilisateur et de fournir des droits d'accès appropriés. Les tokens JWT (JSON Web Token) sont un standard ouvert (RFC 7519) qui permet d'échanger des informations de manière sécurisée entre deux parties. Avec JWT, vous pouvez transmettre des informations d'utilisateur vérifiables et reliables.

Un token JWT est un moyen simple et compact de représenter des informations entre deux parties. L’information peut être des claims (déclarations) comme l’identité d’un utilisateur ou des droits d'accès.

Les principaux avantages de JWT sont la simplicité, l'autonomie et la sécurité.

  • Simplicité : Un JWT est simplement une chaîne de caractères qui peut être envoyée facilement par HTTP.
  • Autonomie : Un JWT contient toutes les informations nécessaires pour décrire l'utilisateur et ses droits, ce qui le rend autonome. Cela permet de réduire le nombre d'opérations nécessaires sur le serveur pour récupérer les informations de l'utilisateur.
  • Sécurité : Un JWT peut être signé et chiffré pour assurer la confidentialité des informations.

1.2 Comment fonctionne JWT

Un JWT est composé de trois parties: Header, Payload et Signature.

  • Header contient des métadonnées sur le type de token et l'algorithme utilisé pour le signer.
  • Payload contient les claims, informations sur l'utilisateur et des données supplémentaires.
  • Signature est utilisée pour vérifier que le message n'a pas été modifié pendant le transfert.

Ensemble, ces trois parties forment une chaîne de caractères encodée en base64 qu'on peut facilement envoyer par HTTP.

Ci-dessous, un exemple simple de JWT:

1eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Dans cet exemple, le header est eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9, le payload est eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ et la signature est SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c.

Pour plus d'informations sur JWT, je vous conseille ce lien intéressant: JWT.io

Voilà en résumé pourquoi et comment utiliser JWT pour l'authentification dans vos applications. Dans la section suivante, nous verrons en détail le bundle LexikJWTAuthentication pour Symfony.

2. Présentation du bundle LexikJWTAuthentication

2.1 Principe de LexikJWTAuthentication

LexikJWTAuthentication est un bundle Symfony qui facilite l'authentification à l'aide de JSON Web Tokens (JWT). Ce bundle, comme expliqué par son créateur Guilhem Niot, permet de sécuriser les APIs RESTful en signant la charge utile du token JWT avec une signature cryptographique.

Voici un extrait de code qui illustre le principe de LexikJWTAuthentication :

1// Création d'un token JWT
2$token = JWS::create()
3 ->setPayload([
4 'iat' => time(),
5 'exp' => time() + 3600,
6 'username' => 'your_username',
7 ])
8 ->sign(new Sha256, 'your_secret')
9 ->getToken();

Le bundle LexikJWTAuthentication réalise l'authentification en vérifiant la signature du token. Si la signature est valide, l'utilisateur est authentifié, sinon, une erreur est retournée.

2.2 Installation de LexikJWTAuthentication

Note : Pour commencer à utiliser LexikJWTAuthentication, il est essentiel d'avoir préalablement installé le framework Symfony.

L’installation du bundle LexikJWTAuthentication se réalise en deux étapes simples.

  1. Ajoutez le bundle à votre projet Symfony via Composer :
1$ composer require "lexik/jwt-authentication-bundle"
  1. Ensuite, ajoutez LexikJWTAuthenticationBundle à votre fichier AppKernel.php :
1public function registerBundles()
2{
3 return array(
4 // ...
5 new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle(),
6 // ...
7 );
8}

Important: Il convient de souligner que pour générer votre clé privée et votre clé publique, favorisant ainsi le fonctionnement de LexikJWTAuthentication, il est nécessaire d'installer OpenSSL sur votre serveur. La génération de ces dernières se fera lors de la configuration du bundle.

La mise en place de JWT et du bundle LexikJWTAuthentication favorise une authentification sécurisée, ce qui constitue un atout important pour les applications APIs. Le succès de l'authentification dépend de la validité du token, le rendant sûr et fiable.

3. Mise en place de l'authentification JWT avec LexikJWT

3.1 Configuration de l'authentification JWT

Pour mettre en place l'authentification JWT avec LexikJWT, la première étape consiste à configurer le bundle. Dans le fichier config/packages/lexik_jwt_authentication.yaml, ajoutez les configurations suivantes :

1lexik_jwt_authentication:
2 secret_key: '%env(resolve:JWT_SECRET_KEY)%'
3 public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
4 pass_phrase: '%env(JWT_PASSPHRASE)%'

Ici, nous indiquons l'emplacement des clés privée et publique qui seront utilisées pour générer et vérifier les tokens JWT. La passphrase est nécessaire pour accéder à la clé privée. Ces informations sont stockées en variables d'environnement pour des raisons de sécurité.

Remarque: Ne mettez jamais les clés privée et publique directement dans le code. Elles doivent rester secrètes.

3.2 Génération de la clé privée et de la clé publique

Pour générer la clé privée et la clé publique, vous pouvez utiliser OpenSSL. Ouvrez une console et lancez les commandes suivantes :

1openssl genpkey -out config/jwt/private.pem -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096
2openssl pkey -in config/jwt/private.pem -out config/jwt/public.pem -pubout

La première commande génère la clé privée. Vous pouvez spécifier une phrase secrète (passphrase) qui sera nécessaire pour l'utiliser. La seconde commande extrait la clé publique à partir de la clé privée.

Attention: La génération des clés doit être faite sur la machine où sera déployée l'application. Si les clés sont générées sur une autre machine, elles ne seront pas valides.

Une fois les clés générées, indiquez leurs emplacements dans les variables d'environnement. Par exemple, dans le fichier .env.local :

1JWT_SECRET_KEY=config/jwt/private.pem
2JWT_PUBLIC_KEY=config/jwt/public.pem
3JWT_PASSPHRASE=MaSuperPassphraseSecrete

Vous avez maintenant configuré LexikJWT pour utiliser l'authentification JWT.

4. Implémentation de l'authentification JWT dans une API

4.1 Création des utilisateurs et génération des tokens JWT

Pour mettre en œuvre l'authentification JWT (JSON Web Token), il est d'abord nécessaire de créer un service d'authentification qui générera les tokens JWT pour les utilisateurs. Les tokens sont générés à partir des informations d'identification des utilisateurs que sont le nom d'utilisateur et le mot de passe. Le token JWT se compose de trois parties : l'en-tête, la charge utile et la signature. Chacune de ces parties est encodée en Base64 et séparée par des points.

Voici un exemple basique de code pour la génération d'un JWT:

1namespace App\Service;
2
3use Firebase\JWT\JWT;
4use App\Entity\User;
5
6class TokenService
7{
8 private $secretKey;
9
10 public function __construct(string $secretKey)
11 {
12 $this->secretKey = $secretKey;
13 }
14
15 public function create(User $user): string
16 {
17 $payload = [
18 'user' => [
19 'id' => $user->getId(),
20 ],
21 'iat' => time(), // Time when JWT was issued.
22 'exp' => time() + 3600, // Expiration time.
23 ];
24
25 return JWT::encode($payload, $this->secretKey);
26 }
27}

La méthode create() dans cet exemple génère un token JWT pour un utilisateur donné. L'expiration du token est fixée à une heure après l'émission du token.

4.2 Authentification à une route sécurisée avec un token JWT

L'utilisation de l'authentification JWT s'étend également à la sécurisation des routes. Une fois le token JWT obtenu, il est utilisé pour valider les demandes à l'API.

Un exemple de route sécurisée avec JWT pourrait être défini de la manière suivante:

1namespace App\Controller;
2
3use Symfony\Component\HttpFoundation\Request;
4use Symfony\Component\HttpFoundation\Response;
5use Symfony\Component\Routing\Annotation\Route;
6
7/**
8 * @Route("/api")
9 */
10class ProjectController
11{
12 /**
13 * @Route("/projects", methods={"GET"})
14 */
15 public function getProjects(Request $request): Response
16 {
17 $token = $request->headers->get('Authorization');
18
19 // validate the JWT token here...
20
21 // send the list of projects as a response...
22 }
23}

Dans cet exemple, chaque demande à la route "/api/projects" doit inclure un en-tête "Authorization" avec le token JWT. Le serveur valide alors le token et renvoie une réponse appropriée.

Important: Les informations secrètes pour la génération du token ne doivent en aucun cas être exposées. La sécurisation adéquate des informations sensibles est cruciale pour maintenir un degré d'intégrité et de sécurité élevé pour les services en ligne.

Les implémentations spécifiques peuvent varier en fonction des besoins du projet, des choix de l'équipe de développement, et d'autres facteurs. Néanmoins, un bon point de départ est d'adopter les principes de base établis dans cet exemple. Ainsi, vous pouvez progressivement adapter et affiner votre processus d'authentification à mesure que vous en apprenez davantage sur les subtilités de JWT et de LexikJWT.

5. Utilisation des tokens JWT dans les clients de l'API

5.1 Récupération du token JWT dans le client de l'API

Après avoir généré un token JWT lors de l'authentification d'un utilisateur, il est impératif de le récupérer et de l'utiliser pour les requêtes ultérieures. Cela peut être réalisé de plusieurs façons. L'une des plus courantes consiste à stocker le token dans le local storage du navigateur.

Par exemple, voici un extrait de code illustrant cette méthode :

1axios.post('/api/login', {
2 username: 'user',
3 password: 'pass'
4})
5.then((response) => {
6 // Stockage du token JWT dans le local storage
7 localStorage.setItem('token', response.data.token);
8
9});

Attention, bien que cette méthode soit populaire, elle n'est pas sans failles de sécurité. Les tokens stockés dans le local storage peuvent être vulnérables aux attaques de type XSS (Cross-Site Scripting). Pour cette raison, certaines applications préfèrent stocker les tokens JWT dans les cookies HTTP.

5.2 Utilisation du token JWT pour s'authentifier à l'API

Une fois le token JWT en notre possession, nous pouvons l'utiliser pour authentifier nos requêtes API. La méthode standard consiste à inclure le token JWT dans l'en-tête de la requête HTTP.

Par exemple :

1axios.get('/api/protected', {
2 headers: {
3 'Authorization': 'Bearer ' + localStorage.getItem('token')
4 }
5})
6.then((response) => {
7 console.log(response.data);
8});

Cet exemple illustre comment envoyer une requête GET à une route qui nécessite une authentification. Le token JWT est placé dans l'en-tête 'Authorization'.

Remarque, le préfixe 'Bearer' est une convention utilisée dans le protocole OAuth 2.0 pour l'envoi de types de token spécifiques, tels que JWT.

De plus, il est important de gérer le renouvellement du token JWT avant qu'il n'expire. Plusieurs stratégies peuvent être mises en œuvre, dont certaines incluent l'utilisation de "refresh tokens". Cependant, ces méthodes sont hors du champ de cette section et nécessitent leur propre discussion.

Enfin, pour augmenter la sécurité de notre système, il est recommandé de supprimer le token JWT une fois que l'utilisateur se déconnecte. Cette mesure empêche l'utilisation non autorisée du token même s'il est encore valide. Des études détaillées à ce sujet peuvent être trouvé sur le site officiel de JWT.

En résumé, l'utilisation correcte des tokens JWT est cruciale pour garantir la sécurité de nos APIs. Cela nécessite une bonne connaissance et une application appropriée des principes de sécurité fondamentaux.

6. Gestion des erreurs et des exceptions avec LexikJWT

LexikJWTAuthenticationBundle offre une puissante gestion des erreurs d'authentification et des exceptions lors de l'utilisation des tokens JWT. Il revient à chaque développeur de savoir comment traiter ces scénarios .

6.1 Gestion des erreurs lors de l'authentification

Il n'est pas rare de rencontrer des erreurs lors de l'authentification avec JWT. Ces erreurs peuvent être liées à une multitude de raisons, par exemple :

  • Le token est expiré et n'est donc plus validé par le serveur.
  • Le serveur ne peut pas déchiffrer le token.
  • Le token est corrompu ou modifié.

LexikJWTAuthenticationBundle fournit un ensemble de classes exceptionnelles pour gérer ces cas. Par exemple :

1<?php
2
3use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
4
5try {
6 $data = $encoder->decode($token);
7} catch (JWTDecodeFailureException $e) {
8 switch ($e->getReason()) {
9 case JWTDecodeFailureException::EXPIRED_TOKEN:
10 // Token has expired - log and handle
11 break;
12 case JWTDecodeFailureException::INVALID_TOKEN:
13 // Invalid token - log and handle
14 break;
15 }
16}
17?>

Dans cet exemple, nous essayons de décoder le token et d'extraire les données, mais deux scénarios d'erreur peuvent se produire :

  1. Le token a expiré (EXPIRED_TOKEN).
  2. Le token est invalide (INVALID_TOKEN).

Dans ce contexte, il est important de prendre en compte que chaque situation devrait être logguée et traitée en conséquence.

6.2 Gestion des exceptions lors de l'utilisation du token JWT

Semblable à la gestion des erreurs d'authentification, LexikJWTAuthenticationBundle offre un mécanisme pour gérer les exceptions qui peuvent survenir lors de l'utilisation du token JWT.

Important: L'une des erreurs les plus courantes lors de l'utilisation de JWT est l'erreur JWTNotFoundException. Elle est généralement déclenchée si le token JWT n'est pas trouvé dans la requête.

En plus de l'exception JWTNotFoundException , il existe d'autres exceptions que vous pouvez rencontrer. Voici comment on peut traiter l'exception JWT NotFoundException :

1<?php
2
3use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTNotFoundException;
4
5try {
6 $token = $request->headers->get('Authorization');
7 $data = $encoder->decode($token);
8} catch (JWTNotFoundException $e) {
9 // Token not found - log and handle
10}
11
12?>

Cela illustre comment vous pouvez gérer les exceptions spécifiques en utilisant LexikJWTAuthenticationBundle. Utiliser correctement ses fonctionnalités de gestion des erreurs et des exceptions permet de renforcer la robustesse de vos APIs. L'élaboration de stratégies de traitement appropriées pour chaque type d'erreur et d'exception peut grandement améliorer le taux d'erreur moyen de votre application et l'expérience générale de l'utilisateur.

7. Bonnes pratiques pour la sécurisation des tokens JWT

JWT est un standard industriel ouvert pour représenter les réclamations à transférer entre deux parties. Il est utilisé pour transmettre des informations pouvant être authentifiées et sécurisées grâce à une signature numérique. Cependant, la sécurisation des tokens JWT est à la fois essentielle et délicate. Voici quelques bonnes pratiques pour renforcer la sécurisation des tokens JWT.

7.1 Sécurité des tokens JWT

La sécurité des tokens JWT passe par plusieurs aspects :

  • Le stockage sécurisé des tokens : il est recommandé de stocker les tokens de manière sécurisée et non pas simplement dans le LocalStorage du navigateur. Les attaques Cross-Site Scripting (XSS) peuvent permettre à des pirates d'intercepter ces tokens. Un stockage plus sécurisé peut être atteint en utilisant des cookies sécurisés afin de prévenir les attaques par hameçonnage.

  • L'utilisation de protocoles sécurisés : pour éviter les attaques de type interposition (man-in-the-middle), il est conseillé de transmettre toujours les tokens via un protocole sécurisé comme HTTPS.

  • Expiration du token : les tokens JWT devraient avoir une durée de vie limitée. Les utilisateurs se voient attribuer un nouveautoken expirable à chaque authentification. Une courte durée de vie (par exemple, 15 minutes) réduit le risque qu'un token volé soit utilisé à mauvais escient par un attaquant.

  • La signature des tokens : Les tokens JWT sont signés pour garantir leur intégrité. Veillez à utiliser des algorithmes de signature robustes pour signer vos tokens JWT. Par exemple, préférez les algorithmes de la famille HS256 ou RS256.

7.2 Renouvellement des tokens JWT

Renouveler périodiquement les tokens JWT est une autre bonne pratique pour améliorer la sécurité. Cela permet de limiter la fenêtre de temps durant laquelle un token JWT volé peut être utilisé.

  • Token de rafraîchissement: Au lieu de renouveler le JWT à chaque requête, une bonne pratique est de renouveler le token de manière périodique. Pour cela, vous pouvez utiliser un "refresh token". Ainsi, même si un attaquant parvient à obtenir un token JWT, il sera inutilisable après que le vrai utilisateur aura renouvelé son token.

  • Blacklisting: En cas de détection d'un comportement suspect, comme plusieurs tentatives d'authentification échouées, il peut être utile d'ajouter le token JWT à une liste noire. La blacklist doit être consultée à chaque requête pour vérifier qu'un token n'a pas été révoqué.

  • Rotation des tokens: Une autre pratique consiste à changer le token JWT à chaque requête. Cette méthode, appelée rotation de token, peut aider à empêcher l'utilisation illégitime de tokens égarés.

En suivant ces bonnes pratiques, vous pouvez renforcer la sécurité de vos architectures en utilisant le standard JWT. Cependant, il faut toujours garder à l'esprit que la sécurité est un processus en constante évolution qui nécessite une vigilance et une mise à jour constantes de vos pratiques et outils.

8. Exemples de code concrets pour l'utilisation de JWT et LexikJWT

La mise en pratique est la meilleure façon d'assimiler une notion. Voici donc des exemples de code que vous pouvez utiliser pour comprendre et implémenter des tokens JWT avec le bundle LexikJWTAuthentication.

8.1 Génération d'un token JWT

Lors de la génération du token JWT, vous pouvez utiliser le code suivant à titre d'exemple:

1public function generateToken(User $user, $expiration_time = 3600){
2 $jwt_manager = $this->container->get('lexik_jwt_authentication.jwt_manager');
3 $token = $jwt_manager->create($user);
4 $expiration_date = new \DateTime('+' . $expiration_time . ' seconds');
5 $jwt = $jwt_manager->encode([
6 'username' => $user->getUsername(),
7 'exp' => $expiration_date->getTimestamp()
8 ]);
9 return $jwt;
10}
11

Cette fonction retourne un token qui expirera une heure après sa création.

Note: Il est important de moduler le temps d'expiration du token en fonction de vos besoins.

8.2 Authentification à une route sécurisée avec JWT et LexikJWT

Lors de l'authentification, le code suivant peut être utilisé:

1public function getTokenAction(Request $request){
2 $user = $this->getDoctrine()
3 ->getManager()
4 ->getRepository('App:User')
5 ->findOneBy(['username' => $request->getUser()]);
6 if (!$user) {
7 throw $this->createNotFoundException();
8 }
9 $isValid = $this->get('security.password_encoder')
10 ->isPasswordValid($user, $request->getPassword());
11 if (!$isValid) {
12 throw new BadCredentialsException();
13 }
14 $token = $this->get('lexik_jwt_authentication.encoder')
15 ->encode(['username' => $user->getUsername()]);
16 return new JsonResponse(['token' => $token]);
17}

Ce code vérifie d'abord si l'utilisateur existe puis compare le mot de passe fourni à celui enregistré dans la base de données. Si le mot de passe est correct, un token est généré et renvoyé dans la réponse.

Ces exemples de code devraient vous guider dans la mise en œuvre de l'authentification JWT avec LexikJWT.

9. Conclusion et perspectives

L'authentification est un aspect crucial de la sécurité des APIs. Elle garantit l'accès limité aux fonctionnalités et objets sécurisés, prévient les attaques malveillantes et permet la traçabilité des actions. En incorporant le processus d'authentification dans vos APIs, vous renforcez leur robustesse et leur fiabilité.

9.1 Le rôle crucial de l'authentification des APIs

Dans les applications modernes basées sur API, les jetons JWT (JSON Web Tokens) sont largement utilisés pour l'authentification et l'autorisation. Leur usage a évolué en raison de l'évolution vers des architectures plus décentralisées qui exigent une méthode d'authentification stateless et sécurisée. Les jetons JWT répondent à cette nécessité.

9.2 La contribution de JWT et LexikJWT à la sécurisation des APIs

  • JWT est un standard ouvert pour la création de tokens d'accès qui permettent la propagation de l'identité et des privilèges. Les tokens JWT sont des jetons auto-suffisants qui contiennent toutes les informations nécessaires sur l'utilisateur, évitant ainsi la nécessité de faire des requêtes supplémentaires info utilisateur à la base de données.

  • LexikJWT, d'autre part, est un bundle Symfony qui facilite l'implémentation du JWT dans vos projets Symfony. Il fournit des classes et des méthodes clés pour générer et valider des jetons JWT. Cela rend l'authentification de votre API sécurisée, facile à gérer et à mettre en place.

Pour conclure, JWT et LexikJWT offrent une stack sûre et robuste pour la sécurisation de vos APIs. En combinant ces outils, vous pouvez créer une solution d'authentification capable de protéger efficacement vos ressources tout en simplifiant l'expérience utilisateur.

Dans les perspectives futures, il est recommandé d'étudier et de mettre en œuvre des mesures supplémentaires de sécurité autour de JWT, tels que le renouvellement automatique de jetons, le blocage des jetons volés et la mise à jour régulière des clés secrètes pour maintenir le niveau le plus élevé de sécurité pour votre API.

Note : Pour plus d'informations sur l'authentification avec JWT, consultez ce guide.

4.7 (33 notes)

Cet article vous a été utile ? Notez le