入って来い! ログインとパスワードなしの認証、「私を記憶する」

このトピックを取り上げたので、標準のremember-me関数の実装を改善する方法を思い出したいと思います。



前に提案された両方のオプションは、1つの重要なポイントを考慮していません。トークンが盗まれた場合はどうなりますか? remember meトークンによる認証の通常の実装では、そのようなトークンを受け取った攻撃者は無期限にサイトにアクセスし、被害者は盗難についても知りません...



どうする?



Barry Jaspanは、改善されたremember-me認証オプションを提案しました。



要するに、別のタイプのトークン-シリーズが追加されます。 ランダムに生成する必要があり、通常のトークンと同じようにできます。 このトークンとトークンの主な違いは、トークンによる認証が成功した後もトークンが変更されないことです。



zerkmsのサンプルコード



収納テーブル:

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
      
      





そして、例のあるクラス

 <?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)); } }
      
      







どのように機能しますか?



ユーザーはCookieにトークンとシリーズを保存しており、スクリプトはそれらをデータベースで提供されているものと比較します。 一致すれば、認証は成功します。 ユーザーは、以前のシリーズで新しいトークンを取得します。 トークンが異なり、シリーズが同じである場合、このアカウントのremember-meエントリをすべて削除し、おそらくトークンが盗まれたことをユーザーに通知します。



ps:これは既成のソリューションではなく、例です。



All Articles