質問
PHPアプリケーションでの多重ログインを防止したい。
まず、ユーザーテーブルにログインステータス(アクティブ、非アクティブ)を作成します。
ユーザー A がログインすると、ユーザーのステータスは「アクティブ」に設定され、ユーザーがログアウトすると、ステータスは「非アクティブ」に設定されます。別のクライアントが同じユーザー アカウントを使用してログインしようとすると、ユーザー テーブルをチェックします。ユーザーがまだアクティブな場合、エラー ログインがユーザーに送信されます。
ユーザーがログアウトをクリックしなかったためにブラウザを閉じると、ユーザー テーブルのステータスが更新される可能性があるという問題が発生しました。
これについて何か提案はありますか?
解決
(ここでのテクニックはまだある程度有効ですが、注意してください。ユーザーが指定した値を SQL クエリに組み込むより安全な手段があるため、PHP サンプルをそのままコピーしないでください)
ユーザーがアクティブか非アクティブかを保存する代わりに、アクションごとにユーザーと照合できる何らかの属性を保存することをお勧めします。たとえば、ユーザーが認証を必要とする何かを行おうとするたびに、続行する前にこの属性が一致するかどうかがチェックされます。
次のことを行うことをお勧めします。
まず、ログインするたびにユーザーを一意に識別するためのハッシュを作成します。私はそれを想像します sha1
の time()
衝突を避けるには十分でしょう。何を選択する場合でも、ログインしている別のユーザーが同じハッシュを受信する可能性が信じられないほど低くなるよう、十分に変化していることを確認してください (たとえば、IP アドレスやブラウザのユーザー エージェントは変化しないため、ハッシュしないでください)十分)。
次に、このハッシュをデータベースとユーザーのデータベースに保存します。 セッション ログイン時。誰かがログインするたびにハッシュが異なるはずなので、これを行うと、前のユーザーが効果的に「ログアウト」されます。
セッションを使用しているため、セッション データに対してユーザーを識別する一意の ID を含む Cookie がユーザーのブラウザに自動的に配置される必要があります。Cookie の内容はあまり重要ではありません。
次に、という関数を作成します。 authenticateUser()
または同様のもので、ユーザーが認証されていることを確認するためにすべてのスクリプトの開始時に呼び出されます。このスクリプトはデータベースにクエリを実行し、ユーザーの ID を持つユーザーがユーザーのハッシュと一致するハッシュを持っているかどうかを確認する必要があります。
例えば:
function authenticateUser($id, $hash, $databaseLink) {
# SQL
$sql = 'SELECT EXISTS(
SELECT 1
FROM `tbl_users`
WHERE `id` = \''.mysql_real_escape_string($id).'\'
AND `hash` = \''.mysql_real_escape_string($hash).'\'
LIMIT 1
);';
# Run Query
if ($query = mysql_query($sql, $databaseLink)) {
# Get the first row of the results
# Assuming 'id' is your primary key, there
# should only ever be one row anyway.
$result = mysql_fetch_row($query);
# Casting to boolean isn't strictly necessary here
# its included to indicate the mysql result should
# only ever been 1 or 0.
return (bool)($result[0]);
} else {
# Query error :(
return false;
}
}
次に、単に通過します authenticateUser()
ユーザーの ID
, hash
(セッションデータごとに) database link
(データベース接続の場合は、事前に開いておく必要があります)。
もし authenticateUser()
戻り値 true
, 、ユーザーは認証されます。もし false
, 、ユーザーがそうでないか、データベースが使用できないか、SQL エラーが発生しています。
ただし、データベース リクエストはページ リクエストごとに 1 回送信されるため、サーバーの負荷が増加することに注意してください。常に何千人ものユーザーがログインしている巨大なプロジェクトでこれを行うのは、おそらくそれほど賢明ではありません。きっと誰かが改善点を提案してくれると思います。
また、Cookie を決して信頼すべきではないため、Cookie の有効期限が切れるのを待つことは、非アクティブなユーザーを強制的にログアウトさせる最善の方法ではありません。代わりに、という列を追加できます。 last_active
ユーザーが認証されるたびに更新できます。これによりサーバーの負荷も増加しますが、古いログインを手動で上書きできるようになります。 hash
たとえば、3 時間非アクティブだったユーザーの場合。
他のヒント
これが解決策です しません 定数が必要 データベースアクセス 働くために...
(これにより、ページをリクエスト/更新するたびに session_id() をデータベース値と照合する必要がなくなり、データベース/サーバーのストレスが軽減されます)...
1. ログイン時に、このユーザーの DB に保存されている既存の session_id を取得し、これを実行します。
session_id("the pre-existing session id in the database goes here");
session_start();
session_destroy();
2. 次に、新しいセッションを開始し、この新しい session_id をデータベースに保存し、前のセッション ID を上書きします。これにより、アクティブなユーザーがいる場合は、そのユーザーの前のセッションがログアウトされます (このアカウントを使用している他のユーザーが事実上ログアウトされます)。
試してみて、うまくいくかどうか教えてください!!
モデルを変更して、最新のユーザーのみがログインできるようにすることができます。
各ユーザーの最新のセッション ID を記録すると、ユーザーが 2 回目にログインするときに、現在存在するセッションを破棄して、事実上ログアウトできます。
通常のユーザーにとっては、物事は「正常に動作している」ように見えます。「異常な」ユーザーがログイン資格情報を配布するのを防ぎたい場合、これは抑止力となるはずです。
ログインしようとしたときに、過去数分間アクティブであったかどうかを確認する必要があります。これは、lastonline スタンプを使用して実行でき、ユーザー テーブル内のすべてのページ リクエストに設定する必要があります。
JavaScript を使用していない場合は、ログオン時にユーザーが過去 15 分間アクティブだったかどうかを確認できます。そうでない場合は、新しいユーザーとしてログインできます。
JavaScriptを使って行うこともできます。1 分ごとに呼び出される ajax 呼び出しを実行します。
<script>
setInterval(function() {
// do the ajax call
}, 60000);
</script>
この呼び出しを、ユーザー データベース内の lastonline スタンプを編集するスクリプトに渡します。ログインしようとすると、最後のオンラインスタンプが分を超えているかどうかをユーザーデータベースで確認し、ログインできるかどうかを確認します。これは、ページにアクセスしているものの、最後の 15 分間はアクティブではなく、他の人にログインさせたくない場合に役立ちます。
一意の ID を作成し、データベースに保存する必要があります。私がやったことも創作でした。1 つはセッション変数に保存し、それを使用してセッション ハイジャックを防止し、もう 1 つはデータベースに保存して多重ログインを防止します。次のコードは一意の ID を作成します。
$unique_id = sha1('xzr4'.gethostbyaddr($_SERVER['REMOTE_ADDR']).$random_string.$_SERVER['HTTP_USER_AGENT'].'f8k2');
一意の ID が一致しない場合は、ユーザーを単にログアウトします。
ログインしているユーザーを追跡するためにクライアント側の JavaScript を使用することは信頼できません。
データベースに lastlogindate フィールドを作成し、それをユーザーの最終ログイン タイムスタンプで更新するだけでも、同じ結果が得られます。
ログインを試行するたびに、now()-$lastlogindate > predependent_timeout の場合は新しいログインを受け入れる必要があり、そうでない場合は拒否する必要があります。
このソリューションはプログラマのソリューションに似ていますが、セッションを切り替える必要はありません。すべてのページでデータベースにアクセスする必要はなく、ログアウトに失敗したユーザーがロックアウトされることもありません。
sessionID のフィールドをデータベース内のユーザー テーブルに追加します。
session_start() を呼び出す前に、デフォルトのセッション ハンドラーを設定します (コードの次の行が機能するために必要です)。
session_set_save_handler(new \SessionHandler());
ログインが成功するたびに、保存されている $sessionID をデータベースから取得します。次のコマンドを使用して古いセッションを破棄します。
(new \SessionHandler())->destroy($sessionID);
次のコマンドを使用して新しいセッション ID を取得します。
$sessionID = session_id();
新しいセッション ID をデータベースに保存します。