📜 ⬆️ ⬇️

Come in! Authentication without login and password, "remember me"

Since we raised this topic, I would like to remind you how to improve the implementation of the standard remember-me function.

Both of the previously proposed options do not take into account one important point, what if a token is abducted? In the normal implementation of authentication through remember me token, the attacker, having received such a token, will get access to the site for an unlimited time, and the victim will not even know about the fact of theft ...

What to do?


Barry Jaspan proposed an improved version of remember-me authentication.

In short, another type of token is added - series. You need to generate it randomly, as you can with the usual token. The main difference between it and the token is that it does not change after successful authentication through the token.
')
For example code from zerkms :

Storage table:
CREATE TABLE test.one_time_auth( token CHAR (32), series CHAR (32), user_id INT (11) UNSIGNED NOT NULL, expire DATETIME DEFAULT NULL, PRIMARY KEY (series) ) ENGINE = INNODB 

And a class with an example
 <?php $db = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', ''); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $auth = new one_time_auth($db); list($token, $series) = $auth->remember(10, null, '2010-12-31'); $user_id = $auth->remind($token, $series); list($token, $series) = $auth->remember(10, $series, '2010-12-31'); $user_id = $auth->remind($token, $series); echo $user_id; list($token, $series) = $auth->remember(10, $series, '2010-12-31'); try { $user_id = $auth->remind('wrongone', $series); } catch (ThiefAssumedException $e) { echo 'We think your cookie was stolen. Please, log in again.'; } $user_id = $auth->remind('wrongone', 'wrongone'); // do nothing class ThiefAssumedException extends Exception {} class one_time_auth { /** * @var PDO */ private $db; public function __construct(PDO $db) { $this->db = $db; } public function remember($user_id, $series = null, $expire = null) { $sql = 'INSERT INTO one_time_auth (token, series, user_id, expire) VALUES (:token, :series, :user_id, :expire)'; $stmt = $this->db->prepare($sql); while (true) { try { $stmt->execute(array( ':token' => $token = $this->generateToken(), ':series' => $series = $series == null ? $this->generateToken() : $series, 'user_id' => $user_id, 'expire' => $expire )); break; } catch (PDOException $e) {} } return array($token, $series); } public function remind($token, $series) { $sql = 'SELECT user_id, token FROM one_time_auth WHERE series = :series AND (expire IS NULL OR expire >= NOW()) LIMIT 1'; $stmt = $this->db->prepare($sql); $stmt->execute(array('series' => $series)); if ($row = $stmt->fetch()) { if ($row['token'] != $token) { $stmt = $this->db->prepare('DELETE FROM one_time_auth WHERE user_id = :user_id'); $stmt->execute(array('user_id' => $row['user_id'])); throw new ThiefAssumedException(); } $stmt = $this->db->prepare('DELETE FROM one_time_auth WHERE series = :series'); $stmt->execute(array('series' => $series)); return $row['user_id']; } } private function generateToken() { return md5(uniqid('', true)); } } 


How it works?


The user in cookies stores token and series, the script compares them with those provided in the database. If they match, then authentication is successful. The user gets a new token from the previous series. If the token is different and the series is the same, then we remove all remember-me entries for this account and inform the user that his token may have been stolen.

ps.: this is not a ready-made solution, but an example.

Source: https://habr.com/ru/post/109475/


All Articles