Dans ce tutoriel, nous allons vous guider pas à pas à travers la mise en place d'une authentification sécurisée en utilisant des JSON Web Tokens (JWT) et des tokens CSRF (Cross-Site Request Forgery), stockés de manière sécurisée dans des cookies. Vous apprendrez à générer et vérifier des tokens JWT et CSRF tout en appliquant les meilleures pratiques de sécurité.
Avant de commencer, assurez-vous de comprendre les bases des JWT et des tokens CSRF. Nous utiliserons PHP pour ce tutoriel.
Génération du JWT et du CSRF
L'objectif est de générer un token JWT qui contient des informations sensibles comme l'ID utilisateur et les rôles, puis de créer un CSRF token pour prévenir les attaques CSRF. Les deux seront ensuite stockés dans des cookies sécurisés.
Configuration de base
Commençons par préparer un fichier de configuration config.php où nous définirons une clé secrète qui servira à signer les tokens JWT. Cette clé doit rester confidentielle.
<?php
const SECRET = '0hLa83lleBroue11e!';Génération du JWT
Le JWT est constitué de trois parties : un header, un payload, et une signature. Nous allons d'abord créer une classe JWT.php qui sera responsable de la génération et de la vérification de ces tokens.
Voici la méthode generate() qui prend en entrée un header, un payload, et un secret pour générer un JWT signé.
<?php
class JWT
{
/**
* Génération du JWT
* @param array $header Header du token
* @param array $payload Payload du token
* @param string $secret Clé secrète
* @param int $validity Durée de validité (en secondes)
* @return string Token
*/
public function generate(array $header, array $payload, string $secret, int $validity = 86400): string
{
if ($validity > 0) {
$now = new DateTime();
$payload['iat'] = $now->getTimestamp(); // Date de création
$payload['exp'] = $now->getTimestamp() + $validity; // Expiration
}
// Encodage en base64
$base64Header = base64_encode(json_encode($header));
$base64Payload = base64_encode(json_encode($payload));
// Nettoyage des caractères (+, /, =) pour éviter les problèmes dans les URL
$base64Header = str_replace(['+', '/', '='], ['-', '_', ''], $base64Header);
$base64Payload = str_replace(['+', '/', '='], ['-', '_', ''], $base64Payload);
// Génération de la signature
$secret = base64_encode($secret);
$signature = hash_hmac('sha256', "$base64Header.$base64Payload", $secret, true);
$base64Signature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));
// Assemblage du JWT
return "$base64Header.$base64Payload.$base64Signature";
}
// Autres méthodes pour vérifier et récupérer les informations du JWT...
}Le JWT est formé de trois parties encodées en base64 et séparées par des points (.) : le header, le payload, et la signature.
Création d'un token CSRF
Le token CSRF est une chaîne de caractères aléatoires utilisée pour protéger contre les attaques de type CSRF. En PHP, vous pouvez le générer avec random_bytes().
$csrfToken = bin2hex(random_bytes(32));Stockage sécurisé des tokens dans des cookies
Une fois le JWT et le token CSRF générés, nous allons les stocker dans des cookies sécurisés avec des options appropriées pour protéger notre application.
Stockage du JWT dans un cookie sécurisé
Le token JWT sera stocké dans un cookie avec les attributs suivants pour garantir la sécurité :
- HttpOnly : Empêche l'accès au cookie via JavaScript.
- Secure : Assure que le cookie est transmis uniquement via HTTPS.
- SameSite=Strict : Limite le cookie au domaine d'origine pour prévenir les attaques CSRF.
Voici comment faire cela :
setcookie('jwt_token', $jwtToken, [
'expires' => time() + 3600, // Expire dans 1 heure
'path' => '/',
'domain' => '127.0.0.1',
'secure' => true, // Seulement en HTTPS
'httponly' => true, // Non accessible via JavaScript
'samesite' => 'strict' // Limite les attaques CSRF
]);Stockage du token CSRF dans un cookie
Contrairement au JWT, le token CSRF doit être accessible via JavaScript car il sera nécessaire pour les requêtes AJAX. Il est donc stocké dans un cookie sans l'attribut HttpOnly, mais avec les autres mesures de sécurité.
setcookie('csrf_token', $csrfToken, [
'expires' => time() + 3600, // Expire dans 1 heure
'path' => '/',
'secure' => true, // Seulement en HTTPS
'samesite' => 'strict' // Limite les attaques CSRF
]);Envoi et vérification des tokens
Vérification des tokens côté serveur
Dans le fichier verif.php, nous allons vérifier la validité des tokens JWT et CSRF envoyés par le client. Le token JWT est récupéré à partir du cookie, tandis que le token CSRF est récupéré à la fois du cookie et du header HTTP pour assurer une double protection.
// verif.php
<?php
require_once 'includes/config.php';
require_once 'classes/JWT.php';
$jwt = new JWT();
// Récupération des tokens
$jwtToken = $_COOKIE['jwt_token'] ?? '';
$csrfCookie = $_COOKIE['csrf_token'] ?? '';
$csrfHeader = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
// Vérifications
try {
// Vérification de la cohérence des tokens CSRF
if ($csrfCookie !== $csrfHeader) {
throw new Exception('Token CSRF invalide');
}
// Vérification du JWT
if (!$jwt->check($jwtToken, SECRET)) {
throw new Exception('Token JWT invalide');
}
// Si tout est correct, on récupère le payload du JWT
$payload = $jwt->getPayload($jwtToken);
// On valide la requête
echo json_encode([
'status' => 'success',
'user_id' => $payload['user_id']
]);
} catch (Exception $e) {
// Si un des tokens est invalide
http_response_code(401);
echo json_encode(['error' => 'Authentification échouée : ' . $e->getMessage()]);
}Envoi des tokens via AJAX
Lorsque vous effectuez des requêtes AJAX, vous devez envoyer le token CSRF dans les headers HTTP ainsi que dans les cookies pour que le serveur puisse vérifier que la requête provient d'une source légitime.
Exemple d'envoi avec fetch() :
const csrfToken = getCookie('csrf_token'); // Fonction pour récupérer le cookie CSRF
fetch('verif.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken // On envoie aussi le token dans le header
},
body: JSON.stringify({ message: 'Données sécurisées' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Erreur:', error));Conclusion
Grâce à ce tutoriel, vous avez mis en place une sécurisation robuste de votre application web avec JWT et tokens CSRF, en vous assurant que :
- Les tokens JWT sont sécurisés et non accessibles via JavaScript.
- Les attaques CSRF sont efficacement contrées grâce au stockage et à la vérification des tokens CSRF.
- Les cookies sont correctement protégés avec les options HttpOnly, Secure, et SameSite.
Cette approche permet de renforcer considérablement la sécurité des applications web modernes et de protéger les utilisateurs contre les attaques fréquentes sur le web.
Obtenir de l'aide
Pour obtenir de l'aide, vous pouvez accéder au serveur Discord pour une entraide par chat.