質問

私の Web アプリケーションはセッションを使用して、ユーザーがログインした後の情報を保存し、ユーザーがアプリ内でページからページに移動するときにその情報を維持します。この特定のアプリケーションでは、 user_id, first_name そして last_name その人の。

ログイン時に「ログインしたままにする」オプションを提供したいと考えています。これにより、ユーザーのマシンに 2 週間 Cookie が保存され、ユーザーがアプリに戻ったときに同じ詳細でセッションが再開されます。

これを行うための最良のアプローチは何でしょうか?保存したくない user_id これにより、あるユーザーが別のユーザーの ID を偽造することが容易になるように思われるためです。

役に立ちましたか?

解決

OK、率直に言ってみましょう:この目的でユーザー データ、またはユーザー データから派生したものを Cookie に入れている場合は、何か間違ったことをしていることになります。

そこには。私は言いました。それでは、実際の答えに移りましょう。

ユーザーデータをハッシュすることの何が悪いのですか?そうですね、結局のところ、露出面と隠蔽によるセキュリティの問題になります。

あなたが攻撃者であることを少し想像してください。セッション上のリメンバーミー用に設定された暗号化 Cookie が表示されます。幅は 32 文字です。やあ。それはMD5かもしれません...

また、彼らがあなたが使用したアルゴリズムを知っていると少し想像してみましょう。例えば:

md5(salt+username+ip+salt)

これで、攻撃者が行う必要があるのは、「ソルト」 (実際にはソルトではありませんが、後で詳しく説明します) をブルート フォースで攻撃することだけです。これで、攻撃者は、自分の IP アドレスに任意のユーザー名を使用して、必要なすべての偽のトークンを生成できるようになります。でも、塩をブルートフォースするのは難しいですよね?絶対に。しかし、現代の GPU はこの点で非常に優れています。そして、十分なランダム性を持たない限り (十分な大きさにする)、城の鍵もろともすぐに落ちてしまいます。

つまり、あなたを守ってくれるのは塩だけですが、塩はあなたが思っているほどあなたを守ってくれないのです。

ちょっと待って!

これらすべては、攻撃者がアルゴリズムを知っていることが前提となっていました。秘密で紛らわしいものであれば、安全ですよね? 間違っている. 。この考え方には次のような名前があります。 隠蔽によるセキュリティ, 、どちらにする必要がありますか 一度もない 頼りにされる。

より良い方法

より良い方法は、ID を除いてユーザーの情報を決してサーバーから出さないことです。

ユーザーがログインすると、大きな (128 ~ 256 ビット) ランダム トークンが生成されます。それを、トークンをユーザー ID にマップするデータベース テーブルに追加し、Cookie でクライアントに送信します。

攻撃者が別のユーザーのランダムなトークンを推測したらどうなるでしょうか?

さて、ここで少し計算してみましょう。128 ビットのランダム トークンを生成しています。つまり、次のようなものがあります。

possibilities = 2^128
possibilities = 3.4 * 10^38

さて、その数字がどれほど途方もなく大きいかを示すために、インターネット上のすべてのサーバー (今日では 50,000,000 サーバーとしましょう) がそれぞれ 1 秒あたり 10 億の速度でその数字をブルートフォース攻撃しようとしていると想像してみましょう。実際には、このような負荷がかかるとサーバーが壊れてしまいますが、実際に試してみましょう。

guesses_per_second = servers * guesses
guesses_per_second = 50,000,000 * 1,000,000,000
guesses_per_second = 50,000,000,000,000,000

つまり、1 秒あたり 50 京の推測が行われます。早いですね!右?

time_to_guess = possibilities / guesses_per_second
time_to_guess = 3.4e38 / 50,000,000,000,000,000
time_to_guess = 6,800,000,000,000,000,000,000

つまり 6.8 セクスティリオン秒...

これをより親しみやすい数字に落とし込んでみましょう。

215,626,585,489,599 years

あるいはさらに良いのは:

47917 times the age of the universe

そう、それは宇宙年齢の 47917 倍です...

基本的には割れることはありません。

要約すると次のようになります。

私がお勧めするより良いアプローチは、Cookie を 3 つの部分に分けて保存することです。

function onLogin($user) {
    $token = GenerateRandomToken(); // generate a token, should be 128 - 256 bit
    storeTokenForUser($user, $token);
    $cookie = $user . ':' . $token;
    $mac = hash_hmac('sha256', $cookie, SECRET_KEY);
    $cookie .= ':' . $mac;
    setcookie('rememberme', $cookie);
}

次に、検証するには次のようにします。

function rememberMe() {
    $cookie = isset($_COOKIE['rememberme']) ? $_COOKIE['rememberme'] : '';
    if ($cookie) {
        list ($user, $token, $mac) = explode(':', $cookie);
        if (!hash_equals(hash_hmac('sha256', $user . ':' . $token, SECRET_KEY), $mac)) {
            return false;
        }
        $usertoken = fetchTokenByUserName($user);
        if (hash_equals($usertoken, $token)) {
            logUserIn($user);
        }
    }
}

注記:データベース内のレコードを検索するためにトークン、またはユーザーとトークンの組み合わせを使用しないでください。必ずユーザーに基づいてレコードをフェッチし、後でフェッチされたトークンを比較するためにタイミングセーフな比較関数を使用してください。 タイミング攻撃の詳細.

さて、それは とても 重要なのは SECRET_KEY 暗号化秘密であること (次のようなものによって生成される) /dev/urandom および/または高エントロピー入力から得られます)。また、 GenerateRandomToken() 強力なランダム ソースである必要があります (mt_rand() あまり強くありません。次のようなライブラリを使用します。 ランダムリブ または ランダム互換, 、 または mcrypt_create_iv()DEV_URANDOM)...

hash_equals() 防ぐことです タイミング攻撃。PHP 5.6 より前の PHP バージョンを使用している場合、関数 hash_equals() はサポートされていません。この場合は交換できます hash_equals() timingSafeCompare 関数を使用すると、次のようになります。

/**
 * A timing safe equals comparison
 *
 * To prevent leaking length information, it is important
 * that user input is always used as the second parameter.
 *
 * @param string $safe The internal (safe) value to be checked
 * @param string $user The user submitted (unsafe) value
 *
 * @return boolean True if the two strings are identical.
 */
function timingSafeCompare($safe, $user) {
    if (function_exists('hash_equals')) {
        return hash_equals($safe, $user); // PHP 5.6
    }
    // Prevent issues if string length is 0
    $safe .= chr(0);
    $user .= chr(0);

    // mbstring.func_overload can make strlen() return invalid numbers
    // when operating on raw binary strings; force an 8bit charset here:
    if (function_exists('mb_strlen')) {
        $safeLen = mb_strlen($safe, '8bit');
        $userLen = mb_strlen($user, '8bit');
    } else {
        $safeLen = strlen($safe);
        $userLen = strlen($user);
    }

    // Set the result to the difference between the lengths
    $result = $safeLen - $userLen;

    // Note that we ALWAYS iterate over the user-supplied length
    // This is to prevent leaking length information
    for ($i = 0; $i < $userLen; $i++) {
        // Using % here is a trick to prevent notices
        // It's safe, since if the lengths are different
        // $result is already non-0
        $result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i]));
    }

    // They are only identical strings if $result is exactly 0...
    return $result === 0;
}

他のヒント

セキュリティに関する通知:確定的データの MD5 ハッシュに基づいて Cookie を作成するのは悪い考えです。CSPRNG から派生したランダムなトークンを使用することをお勧めします。見る ircmaxellさんの答え より安全なアプローチについては、この質問に答えてください。

通常、私は次のようなことをします。

  1. ユーザーは「ログインしたままにする」でログインします
  2. セッションの作成
  3. 以下を含む SOMETHING という名前の Cookie を作成します。md5(salt+ユーザー名+ip+salt) と ID を含む somethingElse という名前の Cookie
  4. クッキーをデータベースに保存する
  5. ユーザーは何かをしてから離れる ----
  6. ユーザーが戻り、SomethingElse Cookie を確認し、存在する場合はそのユーザーのデータベースから古いハッシュを取得し、Cookie の内容を確認します。データベースからのハッシュと一致するものがあり、新しく計算されたハッシュ ( ip) したがって:cookieHash==databaseHash==md5(salt+username+ip+salt)、存在する場合は 2 に、そうでない場合は 1 に進みます。

もちろん、別の Cookie 名などを使用することもできます。また、Cookie の内容を少し変更することもできますが、簡単に作成されないように注意してください。たとえば、ユーザーの作成時に user_salt を作成し、それを Cookie に入れることもできます。

また、md5 (またはほぼすべてのアルゴリズム) の代わりに sha1 を使用することもできます。

導入

あなたのタイトル 「ログインしたままにする」 - 最良のアプローチ 最善のアプローチを検討している場合は、次の点を考慮する必要があるため、どこから始めればよいのかわかりません。

  • 識別
  • 安全

クッキー

Cookie は脆弱です。一般的なブラウザーの Cookie 盗難の脆弱性とクロスサイト スクリプティング攻撃の間では、Cookie が安全ではないことを受け入れる必要があります。セキュリティを向上させるには、次の点に注意する必要があります。 php setcookies などの追加機能があります

ブール セットクッキー ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, ブール値 $http のみ = false ]]]]]])

  • セキュア(HTTPS接続を使用)
  • httponly (XSS 攻撃による個人情報の盗難を軽減)

定義

  • トークン (長さ n の予測できないランダムな文字列。例:/dev/urandom)
  • 参照 (長さ n の予測できないランダムな文字列。例:/dev/urandom)
  • 署名 (HMAC メソッドを使用してキー付きハッシュ値を生成)

シンプルなアプローチ

簡単な解決策は次のようになります。

  • ユーザーは「Remember Me」でログオンしています
  • トークンと署名で発行されたログイン Cookie
  • が戻ってくると署名がチェックされます
  • 署名がOKであれば..次に、ユーザー名とトークンがデータベースで検索されます
  • 有効でない場合..ログインページに戻る
  • 有効な場合は自動的にログインします

上記のケーススタディは、このページに記載されているすべての例を要約していますが、欠点は次のとおりです。

  • Cookieが盗まれたかどうかを知る方法はありません
  • 攻撃者は、パスワードの変更や、個人情報やベーキング情報などのデータの変更などの機密操作にアクセスする可能性があります。
  • 侵害された Cookie は Cookie の有効期間中まだ有効です。

より良いソリューション

より良い解決策は次のとおりです

  • ユーザーはログインしており、自分が選択されていることを覚えています
  • トークンと署名を生成し、Cookie に保存します
  • トークンはランダムであり、1 回の認証に対してのみ有効です。
  • トークンはサイトにアクセスするたびに置き換えられます
  • ログインしていないユーザーがサイトにアクセスすると、署名、トークン、ユーザー名が検証されます。
  • 「Remember me」ログインはアクセスを制限し、パスワードや個人情報などの変更を許可しないようにする必要があります。

コード例

// Set privateKey
// This should be saved securely 
$key = 'fc4d57ed55a78de1a7b31e711866ef5a2848442349f52cd470008f6d30d47282';
$key = pack("H*", $key); // They key is used in binary form

// Am Using Memecahe as Sample Database
$db = new Memcache();
$db->addserver("127.0.0.1");

try {
    // Start Remember Me
    $rememberMe = new RememberMe($key);
    $rememberMe->setDB($db); // set example database

    // Check if remember me is present
    if ($data = $rememberMe->auth()) {
        printf("Returning User %s\n", $data['user']);

        // Limit Acces Level
        // Disable Change of password and private information etc

    } else {
        // Sample user
        $user = "baba";

        // Do normal login
        $rememberMe->remember($user);
        printf("New Account %s\n", $user);
    }
} catch (Exception $e) {
    printf("#Error  %s\n", $e->getMessage());
}

使用されるクラス

class RememberMe {
    private $key = null;
    private $db;

    function __construct($privatekey) {
        $this->key = $privatekey;
    }

    public function setDB($db) {
        $this->db = $db;
    }

    public function auth() {

        // Check if remeber me cookie is present
        if (! isset($_COOKIE["auto"]) || empty($_COOKIE["auto"])) {
            return false;
        }

        // Decode cookie value
        if (! $cookie = @json_decode($_COOKIE["auto"], true)) {
            return false;
        }

        // Check all parameters
        if (! (isset($cookie['user']) || isset($cookie['token']) || isset($cookie['signature']))) {
            return false;
        }

        $var = $cookie['user'] . $cookie['token'];

        // Check Signature
        if (! $this->verify($var, $cookie['signature'])) {
            throw new Exception("Cokies has been tampared with");
        }

        // Check Database
        $info = $this->db->get($cookie['user']);
        if (! $info) {
            return false; // User must have deleted accout
        }

        // Check User Data
        if (! $info = json_decode($info, true)) {
            throw new Exception("User Data corrupted");
        }

        // Verify Token
        if ($info['token'] !== $cookie['token']) {
            throw new Exception("System Hijacked or User use another browser");
        }

        /**
         * Important
         * To make sure the cookie is always change
         * reset the Token information
         */

        $this->remember($info['user']);
        return $info;
    }

    public function remember($user) {
        $cookie = [
                "user" => $user,
                "token" => $this->getRand(64),
                "signature" => null
        ];
        $cookie['signature'] = $this->hash($cookie['user'] . $cookie['token']);
        $encoded = json_encode($cookie);

        // Add User to database
        $this->db->set($user, $encoded);

        /**
         * Set Cookies
         * In production enviroment Use
         * setcookie("auto", $encoded, time() + $expiration, "/~root/",
         * "example.com", 1, 1);
         */
        setcookie("auto", $encoded); // Sample
    }

    public function verify($data, $hash) {
        $rand = substr($hash, 0, 4);
        return $this->hash($data, $rand) === $hash;
    }

    private function hash($value, $rand = null) {
        $rand = $rand === null ? $this->getRand(4) : $rand;
        return $rand . bin2hex(hash_hmac('sha256', $value . $rand, $this->key, true));
    }

    private function getRand($length) {
        switch (true) {
            case function_exists("mcrypt_create_iv") :
                $r = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
                break;
            case function_exists("openssl_random_pseudo_bytes") :
                $r = openssl_random_pseudo_bytes($length);
                break;
            case is_readable('/dev/urandom') : // deceze
                $r = file_get_contents('/dev/urandom', false, null, 0, $length);
                break;
            default :
                $i = 0;
                $r = "";
                while($i ++ < $length) {
                    $r .= chr(mt_rand(0, 255));
                }
                break;
        }
        return substr(bin2hex($r), 0, $length);
    }
}

Firefox と Chrome でのテスト

enter image description here

アドバンテージ

  • セキュリティの向上
  • 攻撃者のアクセスが制限されている
  • Cookie が盗まれた場合、その Cookie は 1 回のアクセスに対してのみ有効です
  • 次に元のユーザーがサイトにアクセスすると、盗難を自動的に検出してユーザーに通知できます

不利益

  • 複数のブラウザ (モバイルおよび Web) を介した永続的な接続はサポートされません。
  • ユーザーは次回のログイン後にのみ通知を受け取るため、Cookie は依然として盗まれる可能性があります。

クイックフィックス

  • 常時接続が必要なシステムごとの承認制度の導入
  • 認証に複数の Cookie を使用する

複数の Cookie のアプローチ

攻撃者が Cookie を盗もうとするときは、特定の Web サイトまたはドメインのみに焦点を当てます。 例.com

しかし、実際には 2 つの異なるドメインからユーザーを認証できます (例.com & 偽追加サイト.com)「Advert Cookie」のように見せます

  • ユーザーのログオン先 例.com 私を覚えていてください
  • ユーザー名、トークン、参照を Cookie に保存
  • ユーザー名、トークン、参照をデータベースに保存します。メムキャッシュ
  • get および iframe 経由で参照 ID を送信します 偽追加サイト.com
  • fakeaddsite.com は参照を使用してデータベースからユーザーとトークンを取得します
  • fakeaddsite.com は署名を保存します
  • ユーザーが fakeaddsite.com から iframe を使用して署名情報を取得して返すとき
  • データを結合して検証を行う
  • ……残りはご存知ですか

2 つの異なる Cookie をどうやって使用できるのかと疑問に思う人もいるかもしれません。まあ、それは可能です、想像してみてください example.com = localhost そして fakeaddsite.com = 192.168.1.120. 。Cookie を検査すると次のようになります

enter image description here

上の画像から

  • 現在訪問しているサイトは localhost です
  • 192.168.1.120 から設定された Cookie も含まれています

192.168.1.120

  • 定義されたもののみを受け入れます HTTP_REFERER
  • 指定されたからの接続のみを受け入れます REMOTE_ADDR
  • JavaScript なし、コンテンツなし。ただし、情報に署名し、Cookie から情報を追加または取得するだけで構成されます。

アドバンテージ

  • 99% の確率で攻撃者を騙します。
  • 攻撃者の最初の試みで簡単にアカウントをロックできる
  • 他の方法と同様に次回ログイン前でも攻撃を防ぐことができます

不利益

  • 1 回のログインに対してサーバーへの複数のリクエスト

改善

  • iframe の使用を完了しました ajax

「remember-me」問題の完璧な解決策を探しているときに見つけた、非常に興味深い記事が 2 つあります。

私はここにの1つの角度を尋ね>、そして答えはあなたが必要とするすべてのトークンベースのタイミングアウトクッキーのリンクにあなたを導くでしょう。

基本的には、クッキーにはuserIdを格納しないでください。あなたは、ユーザがピックアップするために彼らの古いログインセッションを使用してワンタイムトークン(巨大な文字列)を格納します。そして、それは本当に安全にするために、あなたは(パスワード自体を変更するような)重い操作のためのパスワードの入力をお願いします。

私はステファンが言及したアプローチをお勧めします(つまり、の改善永続ログインクッキーのベストプラクティスするのガイドラインに従ってください)と、また、あなたが確認していることをお勧めしますあなたのクッキーです HttpOnlyのクッキーので、彼らには、JavaScript、潜在的に悪質な、にアクセスすることはできません。

これは、ユーザーに関連付けることができますので、あなたのDBに格納し、その後、あなたが知っているかもしれない秘密を持つだけで、ハッシュを生成します。非常にうまく動作するはずです。

古いスレッドですが、依然として有効な懸念です。セキュリティに関するいくつかの良い回答と、「隠蔽によるセキュリティ」の使用の回避に気づきましたが、提供された実際の技術的手法は私の目には十分ではありませんでした。自分のメソッドを貢献する前に言わなければならないこと:

  • 一度もない パスワードを平文で保存してください...絶対に!
  • 一度もない ユーザーのハッシュされたパスワードをデータベース内の複数の場所に保存します。サーバー バックエンドは常にユーザー テーブルからハッシュ化されたパスワードを取得できます。追加の DB トランザクションの代わりに冗長データを保存する方が効率的であるわけではなく、その逆も当てはまります。
  • セッション ID は一意である必要があり、2 人のユーザーがアクセスできないようにする必要があります。 これまで ID を共有する、したがって ID の目的 (運転免許証 ID 番号が他の人と一致する可能性はありますか?)いいえ。これにより、2 つの一意の文字列に基づいて、2 つの部分からなる一意の組み合わせが生成されます。Sessions テーブルでは、PK として ID を使用する必要があります。複数のデバイスを自動サインインに対して信頼できるようにするには、すべての検証済みデバイスのリスト (以下の例を参照) が含まれ、ユーザー名を使用してマップされている信頼できるデバイス用の別のテーブルを使用します。
  • 既知のデータを Cookie にハッシュすることには何の目的もありません。Cookie はコピーできます。私たちが探しているのは、攻撃者がユーザーのマシンを侵害しない限り取得できない本物の情報を提供する、準拠したユーザー デバイスです (もう一度、私の例を参照してください)。ただし、これは、正当なユーザーが自分のマシンの静的情報 (つまり、MAC アドレス、デバイスのホスト名、ブラウザーによって制限されている場合はユーザー エージェントなど)の一貫性が維持されない(またはそもそも偽装されている)場合、この機能を使用することはできません。ただし、これが懸念される場合は、次のようなユーザーに自動サインインを提供しているという事実を考慮してください。 自分自身を一意に識別する, したがって、MAC のスプーフィング、ユーザー エージェントのスプーフィング、ホスト名のスプーフィング/変更、プロキシの背後に隠れるなどの方法で知られることを拒否した場合、それらは識別可能ではないため、自動サービスに対して認証されるべきではありません。これが必要な場合は、使用されているデバイスの ID を確立するクライアント側ソフトウェアにバンドルされているスマート カード アクセスを検討する必要があります。

以上のことを踏まえると、システムで自動サインインを行うための優れた方法が 2 つあります。

まず、すべてを他人に押し付ける、安くて簡単な方法です。たとえば、Google+ アカウントでのログインをサイトでサポートする場合は、ユーザーがすでに Google にサインインしている場合にログインできる、合理化された Google+ ボタンが用意されているはずです (私はいつもそうしているように、この質問に答えるためにここでそうしました) Googleにサインインしています)。ユーザーが信頼できるサポートされている認証システムを使用してすでにサインインしている場合にユーザーが自動的にサインインするようにしたい場合、そのチェックボックスをオンにすると、ロードする前に、クライアント側のスクリプトで対応する「サインイン」ボタンの背後でコードを実行します。 、ユーザー名、セッション ID、およびユーザーに使用される認証子を含む自動サインイン テーブルに一意の ID をサーバーに格納するようにしてください。これらのサインイン方法では AJAX が使用されるため、とにかく応答を待つことになりますが、その応答は検証された応答か拒否のいずれかになります。検証された応答を取得した場合は、それを通常どおり使用し、ログインしているユーザーの読み込みを通常どおり続行します。それ以外の場合、ログインは失敗しましたが、ユーザーには通知せず、ログインしていない状態で続行すると、ユーザーは気付くでしょう。これは、Cookie を盗んだ (または権限を昇格するために Cookie を偽造した) 攻撃者が、ユーザーがサイトに自動サインインしていることを知ることを防ぐためです。

これは安価であり、また、あなたに通知することなく、Google や Facebook などの場所にすでにサインインしている可能性があることを検証しようとするため、一部の人には汚いと思われる可能性があります。ただし、サイトの自動サインインを要求していないユーザーには使用しないでください。この特定の方法は、Google+ や FB などの外部認証のみに使用されます。

ユーザーが認証されたかどうかをバックグラウンドでサーバーに伝えるために外部認証システムが使用されているため、攻撃者は一意の ID 以外のものを取得できず、それ自体は役に立ちません。詳しく説明します:

  • ユーザー「joe」が初めてサイトにアクセスし、セッション ID が Cookie「session」に配置されます。
  • ユーザー「joe」がログインし、権限を昇格し、新しいセッション ID を取得して、Cookie「session」を更新します。
  • ユーザー「joe」は、google+ を使用して自動サインインすることを選択し、Cookie「keepmesignedin」に配置された一意の ID を取得します。
  • ユーザー「joe」は Google にサインイン状態を維持させ、サイトがバックエンドで Google を使用してユーザーを自動サインインできるようにします。
  • 攻撃者は体系的に「keepmesignedin」の一意の ID (これはすべてのユーザーに配布される公知の情報) を試み、他の場所にはサインインしません。「joe」に与えられた一意の ID を試みます。
  • サーバーは「joe」の一意の ID を受信し、DB 内の Google+ アカウントの一致を取得します。
  • サーバーは攻撃者をログイン ページに送信し、Google にログインするための AJAX リクエストを実行します。
  • Google サーバーはリクエストを受信し、その API を使用して攻撃者が現在ログインしていないかどうかを確認します。
  • Google は、この接続を介して現在サインインしているユーザーがいないという応答を送信します。
  • 攻撃者のページが応答を受信すると、スクリプトは URL にエンコードされた POST 値を使用してログイン ページに自動的にリダイレクトします。
  • ログイン ページは POST 値を取得し、空の値と 1970 年 1 月 1 日の有効期限を設定した 'keepmesignedin' の Cookie を送信し、自動試行を阻止します。これにより、攻撃者のブラウザは Cookie を単純に削除します。
  • 攻撃者には通常の初回ログイン ページが与えられます。

何はともあれ、攻撃者が存在しない ID を使用したとしても、検証された応答が受信された場合を除き、試行はすべて失敗するはずです。

この方法は、外部認証システムを使用してサイトにサインインするユーザーのために、内部認証システムと組み合わせて使用​​できますし、使用する必要があります。

=========

さて、ユーザーを自動サインインできる独自の認証システムについては、次のようにします。

DB にはいくつかのテーブルがあります。

TABLE users:
UID - auto increment, PK
username - varchar(255), unique, indexed, NOT NULL
password_hash - varchar(255), NOT NULL
...

ユーザー名の長さは 255 文字であることに注意してください。システム内のサーバー プログラムでユーザー名を 32 文字に制限していますが、外部認証システムでは @domain.tld を含むユーザー名がそれより大きい可能性があるため、互換性を最大限にするために電子メール アドレスの最大長をサポートするだけです。

TABLE sessions:
session_id - varchar(?), PK
session_token - varchar(?), NOT NULL
session_data - MediumText, NOT NULL

ログイン時のユーザー名はセッション データに含まれており、プログラムでは null データが許可されていないため、このテーブルにはユーザー フィールドがないことに注意してください。session_id と session_token は、ランダムな md5 ハッシュ、sha1/128/256 ハッシュ、ランダムな文字列を追加してハッシュ化した日時スタンプなど、必要なものを使用して生成できますが、出力のエントロピーは許容できる限り高く保つ必要があります。ブルート フォース攻撃を開始から軽減するため、セッション クラスによって生成されたすべてのハッシュは、追加を試みる前にセッション テーブル内で一致するかどうかをチェックする必要があります。

TABLE autologin:
UID - auto increment, PK
username - varchar(255), NOT NULL, allow duplicates
hostname - varchar(255), NOT NULL, allow duplicates
mac_address - char(23), NOT NULL, unique
token - varchar(?), NOT NULL, allow duplicates
expires - datetime code

MAC アドレスはその性質上、一意であると考えられているため、各エントリが一意の値を持つことは理にかなっています。一方、ホスト名は別のネットワーク上で合法的に複製できます。コンピューター名の 1 つとして「Home-PC」を使用している人は何人いますか?ユーザー名はサーバー バックエンドによってセッション データから取得されるため、操作することは不可能です。トークンに関しては、ページのセッション トークンを生成するのと同じ方法を使用して、ユーザーの自動サインイン用の Cookie 内のトークンを生成する必要があります。最後に、ユーザーが資格情報を再検証する必要がある場合に備えて日時コードが追加されます。ユーザーのログイン時にこの日時を数日以内に更新するか、最後のログインに関係なく強制的に期限切れにして 1 か月程度のみ保持するか、設計の指示に従ってください。

これにより、誰かが自動サインインすることがわかっているユーザーの MAC とホスト名を組織的になりすますことができなくなります。 一度もない ユーザーにパスワード、クリアテキストなどを含む Cookie を保存させます。セッション トークンの場合と同様に、ページ ナビゲーションごとにトークンを再生成します。これにより、攻撃者が有効なトークン Cookie を取得し、それを使用してログインする可能性が大幅に減少します。攻撃者が被害者から Cookie を盗み、ログインするためにセッション リプレイ攻撃を行う可能性があると言おうとする人もいます。攻撃者が Cookie を盗むことができた場合 (これは可能です)、確実にデバイス全体を侵害することになります。つまり、とにかくログインにデバイスを使用することができ、Cookie を完全に盗む目的が無効になります。サイトが HTTPS 上で実行されている限り (パスワード、CC 番号、その他のログイン システムを扱う場合はそうすべきです)、ブラウザ内で可能なすべての保護をユーザーに提供していることになります。

留意すべき点が 1 つあります。自動サインインを使用する場合、セッション データは期限切れになりません。セッションを継続する機能を誤って期限切れにすることはできますが、セッション間で継続することが予想される永続データである場合は、システムへの検証によりセッション データを再開する必要があります。永続セッション データと非永続セッション データの両方が必要な場合は、ユーザー名を PK として永続セッション データ用に別のテーブルを使用し、通常のセッション データと同じようにサーバーにデータを取得させます。別の変数を使用するだけです。

この方法でログインが達成された後も、サーバーはセッションを検証する必要があります。ここで、システムが盗難または侵害された場合の予測をコード化できます。セッション データへのログインのパターンやその他の予期される結果から、アクセスを取得するためにシステムがハイジャックされたか、Cookie が偽造されたという結論に至ることがよくあります。ここに、ISS 技術者がアカウントのロックダウンや自動サインイン システムからのユーザーの自動削除をトリガーするルールを設定して、攻撃者がどのように成功したか、どのように攻撃を遮断するかをユーザーが判断できるよう十分な時間攻撃者を締め出すことができます。

最後に、回復の試行、パスワードの変更、またはしきい値を超えるログインの失敗が発生した場合、ユーザーが適切に検証してこれが発生したことを認識するまで、自動サインインが無効になることを確認してください。

私の回答でコードが提供されることを期待していた人がいたら申し訳ありませんが、ここではそれは起こりません。私はサイトの運営に PHP、jQuery、AJAX を使用していますが、Windows をサーバーとして使用したことはありません...これまで。

私の解決策はこんな感じです。100%防弾というわけではありませんが、ほとんどの場合は防げると思います。

ユーザーが正常にログインすると、次の情報を含む文字列が作成されます。

$data = (SALT + ":" + hash(User Agent) + ":" + username 
                     + ":" + LoginTimestamp + ":"+ SALT)

暗号化する $data, 、タイプを次のように設定します HTTPのみ そしてクッキーを設定します。

ユーザーがサイトに戻ってきたら、次の手順を実行します。

  1. クッキーデータを取得します。Cookie内の危険な文字を削除します。爆発させてください : キャラクター。
  2. 有効性を確認します。Cookie が X 日より古い場合は、ユーザーをログイン ページにリダイレクトします。
  3. Cookieが古くない場合は、最新のパスワード変更時刻をデータベースから取得します。ユーザーの最後のログイン後にパスワードが変更された場合、ユーザーはログイン ページにリダイレクトされます。
  4. パスが最近変更されていない場合は、ユーザーの現在のブラウザ エージェントを取得します。(currentUserAgentHash == cookieUserAgentHash) かどうかを確認します。エージェントが同じ場合は次のステップに進み、そうでない場合はログイン ページにリダイレクトします。
  5. すべてのステップが正常に完了すると、ユーザー名が認証されます。

ユーザーがサインアウトした場合は、この Cookie を削除します。ユーザーが再ログインした場合に新しい Cookie を作成します。

ハッキングを行うために暗号化されたものを Cookie に保存する必要があるのに、暗号化されたものを Cookie に保存するという概念がわかりません。何か不足している場合は、コメントしてください。

「Remember Me」でもこのアプローチを考えています。問題が見つかった場合は、コメントしてください。

  1. 複数のデバイスからログインできるように、ユーザー テーブルとは別に「Remember Me」データを保存するテーブルを作成します。

  2. ログインに成功した場合 (「ログイン」にチェックを入れた状態):

    a) このマシンの UserID として使用する一意のランダムな文字列を生成します。bigUserID

    b) 一意のランダム文字列を生成します。ビッグキー

    c) Cookie を保存します。bigUserID:bigKey

    d) 「Remember Me」テーブルに、次のレコードを追加します。ユーザーID、IPアドレス、bigUserID、bigKey

  3. ログインが必要なものにアクセスしようとしている場合:

    a) Cookie を確認し、一致する IP アドレスを持つ bigUserID と bigKey を検索します。

    b) 見つかった場合は、その人をログインさせますが、ユーザー テーブルに「ソフト ログイン」フラグを設定して、危険な操作があった場合にフル ログインを求めるプロンプトを表示できるようにします。

  4. ログアウト時に、そのユーザーのすべての「Remember Me」レコードを期限切れとしてマークします。

私が確認できる唯一の脆弱性は次のとおりです。

  • 誰かのラップトップを入手し、Cookie を使用して IP アドレスを偽装する可能性があります。
  • 毎回異なる IP アドレスをスプーフィングして、すべてを推測することもできますが、一致する 2 つの大きな文字列がある場合、それは...上記と同様の計算を行うことになります...わかりません...かなりの確率でしょうか?

私はすべての答えを読んで、まだそれが難しい私が行うことになっていたものを抽出することが分かりました。絵の価値は1Kの言葉であるならば、私は、これは他の人がバリーJaspanのの改善永続ログインクッキーのベストプラクティスに基づいた安全な永続ストレージを実装するのに役立ちます願っています

" ここに画像の説明を入力する

ご質問、フィードバック、または提案があれば、

、私は安全な永続的なログインを実現しようとしている初心者のために反映するために、図を更新しようとします。

の実装機能を使用すると、そのユーザーに意味するかを正確に定義する必要があることを意味し、「ミーは、ログインしてください」。 2日(例えば)の代わりに、2時間:最も単純なケースでは、私はセッションがはるかに長いタイムアウトを持っている意味するものを使用します。あなたは、セッションデータのカスタム有効期限時間を設定することができますので、それをするために、あなたは、おそらくデータベースに、独自のセッションストレージが必要になります。そして、あなたは(またはそれ以上)あなたは数日間の周りに固執するクッキーを設定してください、というよりも、彼らは、ブラウザを閉じたときに期限切れにする必要があります。

私はあなたが求めて聞くことができる「なぜ2日?なぜ2週間?」。これは、自動的に有効期限をプッシュしますPHPでセッションを使用しているためです。 PHPでのセッションの有効期限が実際にアイドルタイムアウトがあるためです。

さて、私はおそらく私がセッション自体に格納難しくタイムアウト値を実装し、2週間かそこらで出て、それを見るためにコードを追加して、強制的にセッションを無効にするだろう、と述べました。あるいは、少なくともそれらをログに記録します。これは、ユーザーが定期的にログインするように要求されることを意味します。ヤフーこれを行います。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top