Overview
Comment: | Corrections et utilisation concrète de la session permanente |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dev |
Files: | files | file ages | folders |
SHA1: |
e744ecebef4f88af46074d8d8419acf0 |
User & Date: | bohwaz on 2017-05-11 06:12:47 |
Other Links: | branch diff | manifest | tags |
Context
2017-05-11
| ||
06:57 | Déplacement de la création de table membres_sessions dans schema.sql, qui sera exécuté plus tard de toutes façons check-in: 6b2fdac6d0 user: bohwaz tags: dev | |
06:12 | Corrections et utilisation concrète de la session permanente check-in: e744ecebef user: bohwaz tags: dev | |
06:11 | Utilisation de KD2\Security pour les tokens CSRF check-in: 6fa02716f8 user: bohwaz tags: dev | |
Changes
Modified src/include/lib/Garradin/Membres/Session.php from [bded1148a6] to [35a4c13870].
︙ | ︙ | |||
41 42 43 44 45 46 47 | // Don't start session if it has been already started if (isset($_SESSION)) { return true; } // Only start session if it exists | | > | | | < | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | // Don't start session if it has been already started if (isset($_SESSION)) { return true; } // Only start session if it exists if ($write || isset($_COOKIE[self::SESSION_COOKIE_NAME])) { session_name(self::SESSION_COOKIE_NAME); return session_start(self::getSessionOptions()); } return false; } static public function refresh() { return self::start(true); } static public function get() { try { return new Session; } catch (\LogicException $e) { return false; } } static public function login($id, $passe, $permanent = false) { assert(is_bool($permanent)); |
︙ | ︙ | |||
95 96 97 98 99 100 101 | // vérification du mot de passe if (!Membres::checkPassword(trim($passe), $membre->passe)) { return false; } // vérification que le membre a le droit de se connecter | | | | > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | // vérification du mot de passe if (!Membres::checkPassword(trim($passe), $membre->passe)) { return false; } // vérification que le membre a le droit de se connecter if ($membre->droit_connexion == Membres::DROIT_AUCUN) { return false; } if ($membre->secret_otp) { self::start(true); $_SESSION = []; $_SESSION['otp'] = (object) [ 'id' => (int) $membre->id, 'secret' => $membre->secret_otp, 'permanent' => $permanent, ]; return self::REQUIRE_OTP; } else { $user = self::createUserSession($membre->id); if ($permanent) { self::createPermanentSession($user); } return true; } } static protected function createUserSession($id) { $db = DB::getInstance(); $user = $db->first('SELECT * FROM membres WHERE id = ?;', (int)$id); if (!$user) { throw new \LogicException(sprintf('Aucun utilisateur trouvé avec l\'ID %s', $id)); } $user->droits = new \stdClass; // Récupérer les droits $droits = $db->first('SELECT * FROM membres_categories WHERE id = ?;', (int)$user->id_categorie); foreach ($droits as $key=>$value) { // Renommer pour simplifier $key = str_replace('droit_', '', $key, $found); // Si le nom de colonne contient droit_ c'est que c'est un droit ! if ($found) { $user->droits->$key = (int) $value; } } self::start(true); $_SESSION['user'] = $user; return $user; } /** * Créer une session permanente "remember me" * @param \stdClass $user * @return boolean */ static protected function createPermanentSession(\stdClass $user) { $selector = hash(self::HASH_ALGO, Security::random_bytes(10)); $token = hash(self::HASH_ALGO, Security::random_bytes(10)); $expire = (new \DateTime)->modify('+3 months'); DB::getInstance()->insert('membres_sessions', [ 'selecteur' => $selector, 'token' => $token, 'expire' => $expire, 'id_membre' => $user->id, ]); $token = hash(self::HASH_ALGO, $token . $user->passe); $cookie = $selector . '|' . $token; $options = self::getSessionOptions(); setcookie(self::PERMANENT_COOKIE_NAME, $cookie, $expire->getTimestamp(), $options['cookie_path'], $options['cookie_domain'], $options['cookie_secure'], true); return true; } static public function isOTPRequired() { self::start(); |
︙ | ︙ | |||
249 250 251 252 253 254 255 | $db->update('membres', ['passe' => $password], 'id = :id', ['id' => (int)$id]); return Utils::mail($dest, '['.$config->get('nom_asso').'] Nouveau mot de passe', $message); } public function __construct() { | > | | | < < < < < < < > > > > > > | 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 | $db->update('membres', ['passe' => $password], 'id = :id', ['id' => (int)$id]); return Utils::mail($dest, '['.$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) { self::createUserSession(\Garradin\LOCAL_LOGIN); } // Démarrage session self::start(); if (empty($_SESSION['user'])) { $this->autoLogin(); } if (empty($_SESSION['user'])) { throw new \LogicException('Aucun utilisateur connecté.'); } $this->user = $_SESSION['user']; $this->id = $this->user->id; } protected function getPermanentCookie() { if (empty($_COOKIE[self::PERMANENT_COOKIE_NAME])) { return false; |
︙ | ︙ | |||
315 316 317 318 319 320 321 322 | { return false; } $db = DB::getInstance(); $row = $db->first('SELECT ms.token, ms.id_membre, strftime("%s", ms.expire) AS expire, membres.passe INNER JOIN membres ON membres.id = ms.id_membre | > | > > | | > | > > | | | 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | { return false; } $db = DB::getInstance(); $row = $db->first('SELECT ms.token, ms.id_membre, strftime("%s", ms.expire) AS expire, membres.passe FROM membres_sessions AS ms INNER JOIN membres ON membres.id = ms.id_membre WHERE ms.selecteur = ?;', $cookie->selector); if ($row->expire < time()) { var_dump($row, time()); die('expired'); return $this->logout(); } // On utilise le mot de passe: si l'utilisateur change de mot de passe // toutes les sessions précédentes sont invalidées $hash = hash(self::HASH_ALGO, $row->token . $row->passe); // Vérification du token if (!hash_equals($cookie->token, $hash)) { // Le sélecteur est valide, mais pas le token ? // c'est probablement que le cookie a été volé, qu'un attaquant // a obtenu un nouveau token, et que l'utilisateur se représente // avec un token qui n'est plus valide. // Dans ce cas supprimons toutes les sessions de ce membre pour // le forcer à se re-connecter $db->delete('membres_sessions', 'id_membre = :id', ['id' => (int) $row->id_membre]); return $this->logout(); } // Re-générons un nouveau token et mettons à jour le cookie $token = hash(self::HASH_ALGO, Security::random_bytes(10)); $expire = (new \DateTime)->modify('+3 months'); $db->update('membres_sessions', [ 'token' => $token, 'expire' => $expire, ], 'selecteur = :selecteur AND id_membre = :id_membre', [ 'selecteur' => $cookie->selector, 'id_membre' => $row->id_membre, ]); $hash = hash(self::HASH_ALGO, $token . $row->passe); $new_cookie = $cookie->selector . '|' . $hash; $options = self::getSessionOptions(); setcookie(self::PERMANENT_COOKIE_NAME, $new_cookie, $expire->getTimestamp(), $options['cookie_path'], $options['cookie_domain'], $options['cookie_secure'], true); $this->id = $row->id_membre; self::createUserSession($this->id); return true; } public function logout() { $url = parse_url(\Garradin\WWW_URL); |
︙ | ︙ | |||
391 392 393 394 395 396 397 | setcookie(self::SESSION_COOKIE_NAME, null, -1, $url['path'], $url['host'], false, true); unset($_COOKIE[self::SESSION_COOKIE_NAME]); return true; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | setcookie(self::SESSION_COOKIE_NAME, null, -1, $url['path'], $url['host'], false, true); unset($_COOKIE[self::SESSION_COOKIE_NAME]); return true; } public function editUser($data) { return (new Membres)->edit($this->id, $data, false); } public function getUser($key = null) { if (null === $key) { return $this->user; |
︙ | ︙ |
Modified src/templates/admin/login.tpl from [318915bc90] to [73997fd1e9].
︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <span class="error">Connexion non-sécurisée !</span> <a href="{$own_https_url}">Se connecter en HTTPS (sécurisé)</a> {else} <span class="alert">Connexion non-sécurisée</span> {/if} {/if} </dd> </dl> </fieldset> <p class="submit"> {csrf_field key="login"} <input type="submit" name="login" value="Se connecter →" /> </p> | > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <span class="error">Connexion non-sécurisée !</span> <a href="{$own_https_url}">Se connecter en HTTPS (sécurisé)</a> {else} <span class="alert">Connexion non-sécurisée</span> {/if} {/if} </dd> <dd><label><input type="checkbox" name="permanent" value="1" /> Rester connecté‑e</label></dd> </dl> </fieldset> <p class="submit"> {csrf_field key="login"} <input type="submit" name="login" value="Se connecter →" /> </p> |
︙ | ︙ |
Modified src/www/admin/login.php from [c09efdc59d] to [5749aca48a].
︙ | ︙ | |||
31 32 33 34 35 36 37 | if (!Utils::CSRF_check('login')) { $error = 'OTHER'; } else { if (Utils::post('id') && Utils::post('passe') | | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | if (!Utils::CSRF_check('login')) { $error = 'OTHER'; } else { if (Utils::post('id') && Utils::post('passe') && Membres\Session::login(Utils::post('id'), Utils::post('passe'), (bool) Utils::post('permanent'))) { Utils::redirect('/admin/'); } $error = 'LOGIN'; } } |
︙ | ︙ |
Modified src/www/admin/logout.php from [fc64d98efd] to [2dab3d0640].
1 2 3 4 5 6 | <?php namespace Garradin; const LOGIN_PROCESS = true; require_once __DIR__ . '/_inc.php'; | | < < | 1 2 3 4 5 6 7 8 | <?php namespace Garradin; const LOGIN_PROCESS = true; require_once __DIR__ . '/_inc.php'; $session->logout(); Utils::redirect('/'); |