Overview
Comment: | Ré-écriture récupération de mot de passe : ne plus utiliser de session mais un hash HMAC limité en durée (1 heure mini.) |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dev |
Files: | files | file ages | folders |
SHA1: |
0077fd0f9a624b89241fc239694261a1 |
User & Date: | bohwaz on 2017-08-04 01:39:22 |
Other Links: | branch diff | manifest | tags |
Context
2017-08-04
| ||
05:28 | Dans une asso, certaines dates du wiki étaient encore au format unix timestamp, corrigeons avant de migrer check-in: dc3f733f4e user: bohwaz tags: dev | |
01:39 | Ré-écriture récupération de mot de passe : ne plus utiliser de session mais un hash HMAC limité en durée (1 heure mini.) check-in: 0077fd0f9a user: bohwaz tags: dev | |
2017-08-03
| ||
05:39 | Modernisation cotisations check-in: 4e287c6f32 user: bohwaz tags: dev | |
Changes
Modified src/include/lib/Garradin/Membres/Session.php from [c7ce83b00e] to [ba6c3b5d63].
1 2 3 4 5 6 7 8 9 10 | <?php namespace Garradin\Membres; use Garradin\Config; use Garradin\DB; use Garradin\Utils; use Garradin\Membres; use Garradin\UserException; | > > | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?php namespace Garradin\Membres; use Garradin\Config; use Garradin\DB; use Garradin\Utils; use Garradin\Membres; use Garradin\UserException; use const Garradin\SECRET_KEY; use const Garradin\WWW_URL; use KD2\Security; use KD2\Security_OTP; use KD2\QRCode; class Session { const HASH_ALGO = 'sha256'; const REQUIRE_OTP = 'otp'; protected $cookie; |
︙ | ︙ | |||
253 254 255 256 257 258 259 | static public function recoverPasswordCheck($id) { $db = DB::getInstance(); $config = Config::getInstance(); $champ_id = $config->get('champ_identifiant'); | | | > > | > | | | > | | < | | > | < > | < | | | > > | > > > > > | > | > | > > > > > > > > > > > > > | | | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | static public function recoverPasswordCheck($id) { $db = DB::getInstance(); $config = Config::getInstance(); $champ_id = $config->get('champ_identifiant'); $membre = $db->first('SELECT id, email, passe FROM membres WHERE '.$champ_id.' = ? LIMIT 1;', trim($id)); if (!$membre || trim($membre->email) == '') { return false; } // valide pour 1 heure minimum $expire = ceil((time() - strtotime('2017-01-01')) / 3600) + 1; $hash = hash_hmac('sha256', $membre->email . $membre->id . $membre->passe . $expire, SECRET_KEY, true); $hash = substr(Security::base64_encode_url_safe($hash), 0, 16); $id = base_convert($membre->id, 10, 36); $expire = base_convert($expire, 10, 36); $query = sprintf('%s.%s.%s', $id, $expire, $hash); $message = "Bonjour,\n\nVous avez oublié votre mot de passe ? Pas de panique !\n\n"; $message.= "Il vous suffit de cliquer sur le lien ci-dessous pour recevoir un nouveau mot de passe.\n\n"; $message.= WWW_URL . 'admin/password.php?c=' . $query; $message.= "\n\nSi vous n'avez pas demandé à recevoir ce message, ignorez-le, votre mot de passe restera inchangé."; Utils::mail($membre->email, '['.$config->get('nom_asso').'] Mot de passe perdu ?', $message); return true; } static public function recoverPasswordConfirm($code) { if (substr_count($code, '.') !== 2) { return false; } list($id, $expire, $email_hash) = explode('.', $code); $config = Config::getInstance(); $db = DB::getInstance(); $id = base_convert($id, 36, 10); $expire = base_convert($expire, 36, 10); $expire_timestamp = ($expire * 3600) + strtotime('2017-01-01'); if (time() / 3600 > $expire_timestamp) { return false; } $membre = $db->first('SELECT id, email, passe FROM membres WHERE id = ? LIMIT 1;', (int)$id); if (!$membre || trim($membre->email) == '') { return false; } $hash = hash_hmac('sha256', $membre->email . $membre->id . $membre->passe . $expire, SECRET_KEY, true); $hash = substr(Security::base64_encode_url_safe($hash), 0, 16); if (!hash_equals($hash, $email_hash)) { return false; } $password = Utils::suggestPassword(); $message = "Bonjour,\n\nVous avez demandé un nouveau mot de passe pour votre compte.\n\n"; $message.= "Votre adresse email : ".$membre->email."\n"; $message.= "Votre nouveau mot de passe : ".$password."\n\n"; $message.= "Si vous n'avez pas demandé à recevoir ce message, merci de nous le signaler."; $password = Membres::hashPassword($password); $db->update('membres', ['passe' => $password], 'id = :id', ['id' => (int)$id]); return Utils::mail($membre->email, '['.$config->get('nom_asso').'] Nouveau mot de passe', $message); } public function __construct() { if (empty($_SESSION['user']) && defined('\Garradin\LOCAL_LOGIN') && is_int(\Garradin\LOCAL_LOGIN) && \Garradin\LOCAL_LOGIN > 0) { |
︙ | ︙ |
Modified src/include/lib/Garradin/Utils.php from [04f73b6305] to [b7e5ac2f67].
︙ | ︙ | |||
433 434 435 436 437 438 439 | $subject = '=?UTF-8?B?'.base64_encode($subject).'?='; if (is_array($to)) { foreach ($to as $t) { | | > > > | > > | > > > | | 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | $subject = '=?UTF-8?B?'.base64_encode($subject).'?='; if (is_array($to)) { foreach ($to as $t) { if (!self::_sendMail($t, $suject, $content, $headers)) { throw new \RuntimeException('Impossible d\'envoyer l\'email'); } } } else { if (!self::_sendMail($to, $subject, $content, $headers)) { throw new \RuntimeException('Impossible d\'envoyer l\'email'); } } return true; } static protected function _sendMail($to, $subject, $content, $headers) { if (SMTP_HOST) { $const = '\KD2\SMTP::' . strtoupper(SMTP_SECURITY); if (!defined($const)) { throw new \LogicException('Configuration: SMTP_SECURITY n\'a pas une valeur reconnue. Valeurs acceptées: STARTTLS, SSL, NONE.'); } $secure = constant($const); $smtp = new SMTP(SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, $secure); return $smtp->send($to, $subject, $content, $headers); } else { return mail('bohwaz', $subject, $content, $headers); } } static public function clearCaches($path = false) { if (!$path) { |
︙ | ︙ |
Modified src/templates/admin/password.tpl from [35d28557ff] to [1feb370da9].
1 2 3 4 5 6 7 8 | {include file="admin/_head.tpl" title="Mot de passe oublié ou pas de mot de passe ?"} {if !empty($sent)} <p class="confirm"> Un e-mail vous a été envoyé, cliquez sur le lien dans cet e-mail pour recevoir un nouveau mot de passe. </p> <p class="alert"> | > < | < < < < < < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | {include file="admin/_head.tpl" title="Mot de passe oublié ou pas de mot de passe ?"} {if !empty($sent)} <p class="confirm"> Un e-mail vous a été envoyé, cliquez sur le lien dans cet e-mail pour recevoir un nouveau mot de passe. </p> <p class="alert"> Si le message n'apparaît pas dans les prochaines minutes, vérifiez le dossier Spam ou Indésirables. </p> {elseif !empty($new_sent)} <p class="confirm"> <strong>Un e-mail contenant votre nouveau mot de passe vous a été envoyé.</strong> Si le message n'apparaît pas dans les prochaines minutes, vérifiez le dossier Spam ou Indésirables. </p> <p><a href="{$www_url}admin/login.php">Connexion →</a></p> {else} {form_errors} <form method="post" action="{$admin_url}password.php"> <fieldset> <legend>Recevoir un e-mail avec un nouveau mot de passe</legend> <p class="help"> Inscrivez ici votre {$champ.title}. Nous vous enverrons un message vous indiquant un lien permettant de recevoir un nouveau mot de passe. |
︙ | ︙ |
Modified src/www/admin/password.php from [8205464eee] to [26d86ab12a].
1 2 3 4 5 6 7 8 9 10 11 | <?php namespace Garradin; const LOGIN_PROCESS = true; require_once __DIR__ . '/_inc.php'; $error = false; if (trim(qg('c'))) { | > > > | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <?php namespace Garradin; use Garradin\Membres\Session; const LOGIN_PROCESS = true; require_once __DIR__ . '/_inc.php'; $error = false; if (trim(qg('c'))) { if (Session::recoverPasswordConfirm(qg('c'))) { Utils::redirect('/admin/password.php?new_sent'); } $form->addError('Le lien que vous avez suivi est invalide ou a expiré.'); } elseif (f('recover')) { $form->check('recoverPassword', [ 'id' => 'required' ]); if (!$form->hasErrors()) { if (trim(f('id')) && Session::recoverPasswordCheck(f('id'))) { Utils::redirect('/admin/password.php?sent'); } $form->addError('Ce membre n\'a pas d\'adresse email enregistrée ou n\'a pas le droit de se connecter.'); } } |
︙ | ︙ |