Double authentification en PHP (2FA TOTP)

PrimFX Boris ('PrimFX') Le 31 janvier 2022

Pour renforcer la sécurité de votre système d'authentification, je vous propose de découvrir comment fonctionne l'authentification à deux facteurs TOTP (Time-based One-Time Password) et de l'implémenter en PHP.

Nous utiliserons pour cela la librairie TwoFactorAuth ainsi que l'application Google Authenticator pour générer automatiquement nos codes d'authentification.

Code source :

  • config.php
    <?php
    session_start();
    require('./vendor/autoload.php');
    
    try {
      $db = new PDO("mysql:host=localhost;dbname=double_auth", 'root', 'root');
      $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    } catch(PDOException $e) {
      echo "Connection failed: " . $e->getMessage();
    }
  • index.php
    <form action="register.php" method="POST">
        <input name="email" type="email" placeholder="Email" /><br />
        <input name="password" type="password" placeholder="Mot de passe" /><br />
        <button type="submit">Inscription</button>
    </form>
  • register.php
    <?php
    require('./config.php');
    
    if (!empty($_POST['email']) && !empty($_POST['password'])) {
        $email = $_POST['email'];
        $password = password_hash($_POST['password'], PASSWORD_DEFAULT);
    
        var_dump($email);
        var_dump($password);
    
        $q = $db->prepare('INSERT INTO users (email, password) VALUES (:email, :password)');
        $q->bindValue('email', $email);
        $q->bindValue('password', $password);
        $res = $q->execute();
    
        if ($res) {
            echo "Inscription réussie";
        }
    }
  • login.php
    <?php
    require('./config.php');
    
    use RobThreeAuthTwoFactorAuth;
    
    if (!empty($_POST['email']) && !empty($_POST['password'])) {
        var_dump($_POST);
    
        $email = $_POST['email'];
        $password = $_POST['password'];
        $tfaCode = $_POST['tfa_code'];
    
        $q = $db->prepare('SELECT * FROM users WHERE email = :email');
        $q->bindValue('email', $email);
        $q->execute();
        $user = $q->fetch(PDO::FETCH_ASSOC);
        
        var_dump($user);
        
        if ($user) {
            $passwordHash = $user['password'];
            if (password_verify($password, $passwordHash)) {
                $tfa = new TwoFactorAuth();
                if (!$user['secret'] || $tfa->verifyCode($user['secret'], $tfaCode)) {
                    $_SESSION['user_id'] = $user['id'];
                    header('location:/profile.php');
                    exit();
                } else {
                    echo "Code 2FA invalide";
                }
            } else {
                echo "Identifiants invalides";
            }
        } else {
            echo "Identifiants invalides";
        }
    }
  • profile.php
    <?php
    require('./config.php');
    
    use RobThreeAuthTwoFactorAuth;
    $tfa = new TwoFactorAuth();
    
    if (empty($_SESSION['tfa_secret'])) {
        $_SESSION['tfa_secret'] = $tfa->createSecret();
    }
    $secret = $_SESSION['tfa_secret'];
    
    if (empty($_SESSION['user_id'])) {
        header('location:/');
        exit();
    }
    
    if (!empty($_POST['tfa_code'])) {
        if ($tfa->verifyCode($secret, $_POST['tfa_code'])) {
            $q = $db->prepare('UPDATE users SET secret = :secret WHERE id = :id');
            $q->bindValue('secret', $secret);
            $q->bindValue('id', $_SESSION['user_id']);
            $q->execute();
        } else {
            echo "Code invalide";
        }
    }
    
    $userReq = $db->prepare('SELECT * FROM users WHERE id = :id');
    $userReq->bindValue('id', $_SESSION['user_id']);
    $userReq->execute();
    $user = $userReq->fetch(PDO::FETCH_ASSOC);
    
    ?>
    <h1>Votre profil</h1>
    
    <a href="/logout.php">Déconnexion</a>
    <?php var_dump($user) ?>
    
    <h2>Activation Double Authentification</h2>
    
    <?php if (!$user['secret']): ?>
        <p>Code secret : <?= $secret ?></p>
        <p>QR Code :</p>
        <img src="<?= $tfa->getQRCodeImageAsDataUri('Tuto', $secret) ?>">
        <form method="POST">
            <input type="text" placeholder="Vérification Code" name="tfa_code">
            <button type="submit">Valider</button>
        </form>
    <?php else: ?>
        <p>2FA activée</p>
    <?php endif ?>
  • logout.php
    <?php
    require('./config.php');
    
    $_SESSION = [];
    session_destroy();
    header('location:/');
    exit();

 

Ressources utiles :

 

Sources :


A propos de l'auteur

PrimFX
Boris ('PrimFX')

Je m'appelle Boris, j'ai 22 ans et je suis passionnĂ© d'informatique. Suite Ă  mes Ă©tudes (Licence Informatique puis MSc Computer Science au Trinity College Dublin), je gĂšre l'entreprise Single Quote co-fondĂ©e en 2019 et je profite de mon temps libre pour partager ma passion Ă  travers des vidĂ©os & articles 😃

Votre commentaire

Vous devez ĂȘtre connectĂ© pour poster un commentaire. Se connecter ou CrĂ©er un compte

Commentaires 2