Aller au contenu

TP MFA avec TOTP đŸ”âš“ïžŽ

🎯 Objectifs pĂ©dagogiques

  • Comprendre le fonctionnement d’un second facteur TOTP (Time‑based One‑Time Password).
  • ImplĂ©menter un parcours d’authentification Web : login/mot de passe → saisie du code TOTP.
  • GĂ©nĂ©rer un secret TOTP, un QR Code et vĂ©rifier les codes cĂŽtĂ© serveur.
  • Appliquer des bonnes pratiques (hash de mot de passe, sessions, contrĂŽle d’accĂšs).

1. Travail GuidĂ©âš“ïžŽ

1.1 PrĂ©paration du projet⚓

  1. Créez un dossier mfa-php/ avec la structure :

mfa-php/ ├─ config.php ├─ schema.sql ├─ register.php ├─ login.php ├─ mfa_setup.php ├─ mfa_verify.php ├─ dashboard.php ├─ logout.php └─ helpers/ └─ totp.php 2. Installez la librairie OTPHP avec Composer

👉 Placez vous dans mfa-php/ et exĂ©cutez la commande suivante :
composer require spomky-labs/otphp

  1. Dans MySQL, créez une base demo et créer la table ci-dessous.

1.2 ModĂšle de donnĂ©es⚓

Créez la table users :

CREATE TABLE IF NOT EXISTS users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(255) NOT NULL UNIQUE,
  password_hash VARCHAR(255) NOT NULL,
  mfa_secret VARCHAR(64) DEFAULT NULL,
  mfa_enabled TINYINT(1) NOT NULL DEFAULT 0,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

1.3 SĂ©curitĂ© minimale attendue⚓

  • Hash des mots de passe avec password_hash() + vĂ©rif via password_verify().
  • Sessions sĂ©curisĂ©es (session_start() en dĂ©but de chaque page, vĂ©rifs d’accĂšs).
  • Ne jamais stocker le mot de passe en clair, ni afficher le secret TOTP Ă  l’écran (sauf pour debug en local si besoin).

1.4 Parcours utilisateur Ă  implĂ©menter⚓

1.Inscription (register.php) : email + mot de passe → crĂ©e l’utilisateur.
2.Connexion (étape 1) (login.php) : vérifie email + mot de passe.
> Si mfa_enabled = 0 → redirection vers mfa_setup.php.
> Sinon → redirection vers mfa_verify.php.
3.Activation MFA (mfa_setup.php) :
> GénÚre un secret TOTP.
> Affiche un QR Code à scanner dans l’application mobile (URI otpauth://).
> VĂ©rifie un premier code entrĂ© par l’utilisateur.
> Si valide → mfa_enabled=1.
4.Vérification MFA (mfa_verify.php) : demande le code TOTP à chaque connexion.
5.Une fois validé, accÚs à dashboard.php (zone protégée). logout.php termine la session.

1.5 Liste des fichiers⚓

  • register.php → formulaire email + mot de passe (POST) → redirection login.
  • login.php : formulaire email + mot de passe (POST). Si OK et mfa_enabled=1 ⇒ redirection mfa_verify.php, sinon mfa_setup.php.
  • mfa_setup.php : affiche QR + champ "Code TOTP" → si valide, enregistre mfa_enabled=1 et redirige dashboard.php.
  • mfa_verify.php : champ "Code TOTP" → si valide, session authentifiĂ©e complĂšte → dashboard.php.
  • dashboard.php → page protĂ©gĂ©e avec “Bienvenue ”.

✅ Fonctionnel : inscription, login, setup MFA, vĂ©rification MFA, dĂ©connexion.
✅ Code : sĂ©paration logique, vĂ©rifs d’erreurs, messages clairs.

2. Tests & Validation⚓

  • CrĂ©er un compte, puis se connecter.
  • À la premiĂšre connexion, la page MFA Setup affiche le QR Code : scannez‑le avec votre appli.
  • Saisissez un code TOTP : l’activation doit rĂ©ussir et vous envoyer au dashboard.
  • DĂ©connectez‑vous, reconnectez‑vous : aprĂšs le mot de passe, la page MFA Verify est demandĂ©e ; entrez le code.
  • VĂ©rifiez que l’accĂšs direct Ă  dashboard.php sans TOTP n’est pas possible (contrĂŽles de session).

Bonnes pratiques & Extensions

  • SĂ©curitĂ© :

    • Utiliser password_hash()/password_verify() (dĂ©jĂ  fait).
    • ProtĂ©ger les pages par des contrĂŽles de session stricts.
    • Limiter les tentatives de connexion (ex. compteur + dĂ©lai).
    • Masquer le secret cĂŽtĂ© interface (affichĂ© ici pour pĂ©dagogie seulement).
  • Codes de secours (bonus) : gĂ©nĂ©rer 5× codes alĂ©atoires hashĂ©s en BDD pour rĂ©cupĂ©ration.

  • Souvenir d’appareil (bonus) : cookie signĂ© (HMAC) valide X jours, pour Ă©viter le TOTP Ă  chaque login.
  • DĂ©calage d’horloge : afficher l’heure serveur et recommander la synchronisation de l’horloge client.

đŸ§Ș DĂ©pannage (FAQ rapide)

  • Le QR ne s’affiche pas : vĂ©rifiez l’URL (encodage de l’URI otpauth://).
  • Redirections bizarres : sessions non dĂ©marrĂ©es ? session_start() doit ĂȘtre tout en haut de chaque script (via config.php).

3. Correction⚓

Corrigé & Code de référence

```php <?php // ===== config.php ===== ini_set('display_errors', 1); error_reporting(E_ALL); session_start();

$DB_HOST = '127.0.0.1'; $DB_NAME = 'mfa_demo'; $DB_USER = 'root'; $DB_PASS = '';

try { \(pdo = new PDO("mysql:host=\)DB_HOST;dbname=$DB_NAME;charset=utf8mb4", $DB_USER, $DB_PASS, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); } catch (PDOException $e) { die("Erreur de connexion DB : " . $e->getMessage()); }

function require_login() { if (!isset($_SESSION['user_id'])) { header("Location: login.php"); exit; } }

function is_fully_authenticated() { return isset(\(_SESSION['user_id']) && (!(\)_SESSION['mfa_required'] ?? false)); }

// ===== mfa_setup.php ===== require DIR . '/config.php'; require DIR . '/vendor/autoload.php';

use OTPHP\TOTP;

require_login();

$stmt = $pdo->prepare("SELECT * FROM users WHERE id=?"); \(stmt->execute([\)_SESSION['user_id']]); $user = $stmt->fetch();

if (!$user) { header("Location: login.php"); exit; }

if ($user['mfa_enabled']) { header("Location: dashboard.php"); exit; }

if (empty($user['mfa_secret'])) { $totp = TOTP::create(); $secret = $totp->getSecret(); \(pdo->prepare("UPDATE users SET mfa_secret=? WHERE id=?")->execute([\)secret, $user['id']]); $user['mfa_secret'] = $secret; }

\(totp = TOTP::create(\)user['mfa_secret']); \(totp->setLabel(\)user['email']); $totp->setIssuer("DemoMFA"); $uri = $totp->getProvisioningUri(); \(qr = "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=".urlencode(\)uri);

if ($_SERVER['REQUEST_METHOD'] === 'POST') { \(code = trim(\)_POST['code'] ?? ''); if (\(totp->verify(\)code)) { \(pdo->prepare("UPDATE users SET mfa_enabled=1 WHERE id=?")->execute([\)user['id']]); $_SESSION['mfa_required'] = false; header("Location: dashboard.php"); exit; } else { $error = "Code invalide."; } } ?> <!DOCTYPE html> Setup MFA

Activer MFA

Scannez ce QR Code avec Google Authenticator :

<img src="\(qr ?>" alt="QR Code"><br> <form method="post"> <label>Entrez le code: <input type="text" name="code" required></label> <button type="submit">Activer</button> </form> <?php if (!empty(\)error)) echo "

$error

"; ?>

// ===== mfa_verify.php ===== require DIR . '/config.php'; require DIR . '/vendor/autoload.php';

use OTPHP\TOTP;

require_login();

if (!($_SESSION['mfa_required'] ?? false)) { header("Location: dashboard.php"); exit; }

$stmt = $pdo->prepare("SELECT * FROM users WHERE id=?"); \(stmt->execute([\)_SESSION['user_id']]); $user = $stmt->fetch();

if (!\(user || !\)user['mfa_enabled']) { header("Location: login.php"); exit; }

\(totp = TOTP::create(\)user['mfa_secret']);

if ($_SERVER['REQUEST_METHOD'] === 'POST') { \(code = trim(\)_POST['code'] ?? ''); if (\(totp->verify(\)code)) { $_SESSION['mfa_required'] = false; header("Location: dashboard.php"); exit; } else { \(error = "Code invalide."; } } ?> <!DOCTYPE html> <html lang="fr"> <head><meta charset="UTF-8"><title>Vérification MFA</title></head> <body> <h1>Vérification du code MFA</h1> <form method="post"> <input type="text" name="code" required> <button type="submit">Vérifier</button> </form> <?php if (!empty(\)error)) echo "

$error

"; ?>

// ===== dashboard.php ===== require DIR . '/config.php'; require_login(); if (!is_fully_authenticated()) { header('Location: mfa_verify.php'); exit; } ?> <!DOCTYPE html> Dashboard

Bienvenue,

Votre compte est protĂ©gĂ© par la MFA TOTP ✅

Se déconnecter

```