web-dev-qa-db-ja.com

PHPログインシステム:Remember Me(永続的なCookie)

ログインする前に「記憶する」チェックボックスオプションを追加したい。

ユーザーのブラウザにクッキーを安全に保存する最良の方法は何ですか?

たとえば、Facebookには「remember me」チェックボックスがあり、facebook.comにアクセスするたびにすでにログインしていることがわかります。

現在のログインでは、単純なセッションを使用しています。

39
Karem

この質問はよく聞かれます。ここにいくつかのリンクがあります。

また、この質問への回答で収集されたいくつかの素晴らしいリソースがあります:ウェブサイト認証の決定的なガイド

36
Paul Dixon

更新(2017-08-13):の代わりにselectortokenを分離している理由を理解するためtokenを使用するだけで、SELECTクエリのタイミング攻撃を防ぐためのトークンの分割に関する この記事をお読みください

このブログ投稿で説明されている戦略を抽出します 安全な長期認証について それは多くの分野をカバーしており、私たちは "remember me" 部分。

プリアンブル-データベース構造

次のようなユーザーテーブルとは別のテーブルが必要です(MySQL):

_CREATE TABLE `auth_tokens` (
    `id` integer(11) not null UNSIGNED AUTO_INCREMENT,
    `selector` char(12),
    `token` char(64),
    `userid` integer(11) not null UNSIGNED,
    `expires` datetime,
    PRIMARY KEY (`id`)
);
_

ここで重要なことは、selectortokenが別々のフィールドであることです。

ログイン後

random_bytes()がない場合は、 random_compat のコピーを取得してください。

_if ($login->success && $login->rememberMe) { // However you implement it
    $selector = base64_encode(random_bytes(9));
    $authenticator = random_bytes(33);

    setcookie(
        'remember',
         $selector.':'.base64_encode($authenticator),
         time() + 864000,
         '/',
         'yourdomain.com',
         true, // TLS-only
         true  // http-only
    );

    $database->exec(
        "INSERT INTO auth_tokens (selector, token, userid, expires) VALUES (?, ?, ?, ?)", 
        [
            $selector,
            hash('sha256', $authenticator),
            $login->userId,
            date('Y-m-d\TH:i:s', time() + 864000)
        ]
    );
}
_

ページ読み込み時の再認証

_if (empty($_SESSION['userid']) && !empty($_COOKIE['remember'])) {
    list($selector, $authenticator) = explode(':', $_COOKIE['remember']);

    $row = $database->selectRow(
        "SELECT * FROM auth_tokens WHERE selector = ?",
        [
            $selector
        ]
    );

    if (hash_equals($row['token'], hash('sha256', base64_decode($authenticator)))) {
        $_SESSION['userid'] = $row['userid'];
        // Then regenerate login token as above
    }
}
_

詳細

セレクターには、9バイトのランダムデータ(12文字にエンコードされたbase64)を使用します。これにより、72ビットのキースペースが提供されるため、236 衝突耐性のビット(誕生日攻撃)。これは、ストレージ容量(integer(11) UNSIGNED)よりも16倍大きいです。

実際のオーセンティケーターには、33バイト(264ビット)のランダム性を使用します。これは、すべての実用的なシナリオで予測不可能なはずです。

オーセンティケーターのSHA256ハッシュをデータベースに保存します。これにより、情報漏洩後のユーザーのなりすましのリスクが軽減されます。

ユーザーのCookieに保存されている認証子値のSHA256ハッシュを再計算し、 hash_equals() を使用して保存されたSHA256ハッシュと比較して、タイミング攻撃を防ぎます。

DBルックアップは一定時間ではないため、セレクターを認証子から分離しました。これにより、劇的なパフォーマンスヒットを引き起こすことなく、検索に対するタイミングリークの潜在的な影響が排除されます。

43