index.php (votre application)
\otphp
\lib
hotp.php
otp.php
otphp.php
totp.php
\vendor
base32.php
libs.php
==== Vérifier et synchroniser l'heure du serveur ====
Si votre serveur et votre téléphone ne sont pas synchrones au niveau de l'heure, les codes TOTP seront valides moins longtemps que prévu, voir même pas du tout. Affichez l'heure de votre serveur, et ajustez au besoin la valeur ''$decalage'' afin que l'heure obtenue soit exactement celle de votre téléphone (idéalement à la seconde près).
==== Tester la génération d'un TOTP ====
Maintenant que les heures sont synchro, ajoutons la génération de l'OTP et affichons-le:
';
require_once dirname(__FILE__).'/otphp/lib/otphp.php';
$totp = new \OTPHP\TOTP("DEKGGLZJ",array('interval'=>60));
echo $totp->at($maintenant);
?>
Remarquez que nous avons remis les mêmes paramètres:
* Le secret commun (''DEKGGLZJ'')
* L'interval à 60 secondes ('''interval'=>60'')
* Par défaut, l'algo est SHA1 et le code sur 6 chiffres (Donc inutile de le préciser).
Il devrait vous afficher quelque chose du genre:
2014-07-11 22:53:41
812059
Si l'heure affichée correspond bien à l'heure de votre smartphone, la seconde ligne devrait vous afficher un code identique à celui que vous affiche l'application FreeOTP. Cette valeur n'est pas valide plus de 60 secondes.
Si ce n'est pas le cas, c'est un manque de bol (vous avez généré un code juste à la limite de la période de temps), ou alors vous avez fait une erreur sur l'un de ces trois paramètres:
* Heure exacte entre serveur et smartphone.
* Secret commun
* Interval
En cas de doute, supprimez l'OTP dans l'application FreeOTP et re-crééez-en une nouvelle.
===== Sécuriser un formulaire de login =====
Maintenant que le codes OTP sont générés à l'identique entre serveur et smartphone, nous allons nous en servir pour sécuriser un formulaire de login existant.
==== Notre application ====
Nous allons créer une application fictive qui n'a qu'un seul écran (l'écran de login) et capable de faire une seule chose: Vous dire si votre login et mot de passe sont corrects.
Vous noterez que notre application est particulièrement stupide: Login et mot de passe sont codés en dur dans le programme. Mais peu importe, c'est suffisant pour la démonstration. Si vous entrez bien toto/titi, elle vous répondre: ''Login OK !''.
{{:totp:totp_01.png?nolink|}}
==== Modification du formulaire de login ====
Nous allons maintenant ajouter l'OTP: Ajoutons d'abord un champ OTP au formulaire de login.
OTP:
Le formulaire devient:
==== Modification du traitement du formulaire ====
Nous allons ajouter la fonction suivante:
// Vérifie la valeur OTP
function checkOTP($otp)
{
$decalage = 0; // (en secondes)
$maintenant = time() + $decalage;
require_once dirname(__FILE__).'/otphp/lib/otphp.php';
$totp = new \OTPHP\TOTP("DEKGGLZJ",array('interval'=>60));
return $totp->verify($otp,$maintenant);
}
Cette fonction renvoie ''true'' si le code OTP est valide.
Ajoutons maintenant la vérification de l'OTP à celle du login/mot de passe:
if ( checkLoginPassword($_POST['login'], $_POST['password'] ) && checkOTP($_POST['otp']) )
Le code entier devient donc:
60));
return $totp->verify($otp,$maintenant);
}
// Traitement du formulaire de login:
if (!empty($_POST['login']))
{
if ( checkLoginPassword($_POST['login'], $_POST['password'] ) && checkOTP($_POST['otp']) )
echo "Login OK !";
else
echo "Echec login";
}
?>
==== Test ====
Voici donc notre nouveau formulaire:
{{:totp:totp_02.png?nolink|}}
Entrez login, mot de passe, puis tapez l'application FreeOTP pour qu'elle vous génère un code. Entrez ce code dans le champ 'OTP' et validez: Votre login devrait être accepté.
Vous pouvez refaire le test avec le **bon** login, le **bon** mot de passe mais un code OTP 6 chiffres au hasard: Votre login sera refusé même si le mot de passe est bon.
C'est tout l'intérêt d'OTP: Si une personne vous vole votre mot de passe, elle ne pourra pas l'utiliser pour se connecter car elle n'a pas le générateur OTP de votre smartphone. Elle n'a pu voler qu'un seul des deux facteurs (//ce que je sais//). Cela est utile si le mot de passe est volé sur le réseau, ou sur la machine (keylogger) ou si quelqu'un vous voit le taper.
===== Conseils =====
* Le code OTP ne doit //jamais// remplacer le mot de passe. Il ne peut que le compléter.
* Bien entendu, on ne laisse jamais les mots de passe et secrets OTP directement dans les sources. Stockez-les ailleurs !
* Je vous recommande de toujours afficher la date de l'heure sur votre formulaire de login. Vous pourrez ainsi immédiatement voir s'il y a un décalage avec votre téléphone.
* Il est facile de mettre la vérification OTP sous forme d'une librairie bien à part que vous pourrez appeler depuis différentes applications php. La double authentification sera alors facile à ajouter à la plupart des applications php existantes:
* Simple ajout d'un champ ''OTP'' dans le formulaire de login.
* Un ''require_once'' pour inclure votre librairie.
* Appel à votre fonction ''checkOTP()'' là où le logiciel vérifie login et mot de passe.
===== Exemple =====
==== DokuWiki ====
Voici en exemple comment sécuriser le formulaire de login de DokuWiki avec TOTP. La bonne manière de faire aurait été de créer un plugin d'authentification spécifique, mais j'ai choisi de taper directement dans le code (ce qui obligera à refaire la manip à la prochaine mise à jour, mais c'est assez rapide à faire: 6 lignes à modifier).
Dans ''inc/html.php'':
--- html.php.original Mon Jul 14 13:49:34 2014
+++ html.php Mon Jul 14 19:51:35 2014
@@ -47,6 +47,7 @@
$form->addHidden('do', 'login');
$form->addElement(form_makeTextField('u', ((!$INPUT->bool('http_credentials')) ? $INPUT->str('u') : ''), $lang['user'], 'focus__this', 'block'));
$form->addElement(form_makePasswordField('p', $lang['pass'], '', 'block'));
+ $form->addElement(form_makePasswordField('otp', 'OTP', '', 'block'));
if($conf['rememberme']) {
$form->addElement(form_makeCheckboxField('r', '1', $lang['remember'], 'remember__me', 'simple'));
}
Dans ''inc/auth.php'':
--- auth.php.original Mon Jul 14 13:49:33 2014
+++ auth.php Mon Jul 14 20:14:55 2014
@@ -110,6 +110,7 @@
$evdata = array(
'user' => $INPUT->str('u'),
'password' => $INPUT->str('p'),
+ 'otp' => $INPUT->str('otp'),
'sticky' => $INPUT->bool('r'),
'silent' => $INPUT->bool('http_credentials')
);
@@ -179,6 +180,7 @@
return auth_login(
$evdata['user'],
$evdata['password'],
+ $evdata['otp'],
$evdata['sticky'],
$evdata['silent']
);
@@ -213,7 +215,7 @@
* @param bool $silent Don't show error on bad auth
* @return bool true on successful auth
*/
-function auth_login($user, $pass, $sticky = false, $silent = false) {
+function auth_login($user, $pass, $otp, $sticky = false, $silent = false) {
global $USERINFO;
global $conf;
global $lang;
@@ -228,7 +230,8 @@
if(!empty($user)) {
//usual login
- if($auth->checkPass($user, $pass)) {
+ require_once realpath(dirname(__FILE__).'/../../myotp/myotp.php'); // Lien vers votre librairie OTP
+ if($auth->checkPass($user, $pass) && checkOTP($otp)) {
// make logininfo globally available
$INPUT->server->set('REMOTE_USER', $user);
$secret = auth_cookiesalt(!$sticky, true); //bind non-sticky to session
==== Codiad ====
Dans ''index.php'':
--- index.php.original Sun Jul 13 07:56:04 2014
+++ index.php Tue Jul 15 14:21:30 2014
@@ -125,6 +125,9 @@
+
+
+