6
<?php
session_start();

include("connect.php");

$timeout = 60 * 30;
$fingerprint = md5($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']);

if(isset($_POST['userName']))
{
    $user = mysql_real_escape_string($_POST['userName']);
    $password = mysql_real_escape_string($_POST['password']);
    $matchingUser = mysql_query("SELECT * FROM `users` WHERE username='$user' AND password=MD5('$password') LIMIT 1");
    if (mysql_num_rows($matchingUser))
    {
        if($matchingUser['inactive'] == 1)//Checks if the inactive field of the user is set to one
        {
            $error = "Your e-mail Id has not been verified. Check your mail to verify your e-mail Id. However you'll be logged in to site with less privileges.";
            $_SESSION['inactive'] = true;
        }
        $_SESSION['user'] = $user;
        $_SESSION['lastActive'] = time();
        $_SESSION['fingerprint'] = $fingerprint;
    }
    else
    {
        $error = "Invalid user id";
    }
}
if ((isset($_SESSION['lastActive']) && $_SESSION['lastActive']<(time()-$timeout)) || (isset($_SESSION['fingerprint']) && $_SESSION['fingerprint']!=$fingerprint)
     || isset($_GET['logout'])
    )
{
    setcookie(session_name(), '', time()-3600, '/');
    session_destroy();
}
else
{
    session_regenerate_id(); 
    $_SESSION['lastActive'] = time();
    $_SESSION['fingerprint'] = $fingerprint;
}
?>

これは、 http://en.wikibooks.org/wiki/PHP_Programming/User_login_systemsの修正バージョンです。

ここで何をしsetcookie(session_name(), '', time()-3600, '/');ますか?

ここにバグがあります:私はこのログインフォームを使用します:

<?php 
   if(!isset($_SESSION['user']))
    {
        if(isset($error)) echo $error;
           echo '<form action="' . $_SERVER["PHP_SELF"] . '" method="post">
        <label>Username: </label>
        <input type="text" name="userName" value="';if(isset($_POST['userName'])) echo $_POST["userName"]; echo '" /><br />
        <label>Password: </label>
        <input type="password" name="password" />
        <input type="submit" value="Login" class="button" />
        <ul class="sidemenu">
        <li><a href="register.php">Register</a></li>
        <li><a href="forgotPassword.php">Forgot Password</a></li>
    </ul>
    </form>';
    }
    else
    {
        echo '<ul class="sidemenu">
        <li>' . $_SESSION['user'] . '</li>
        <li><a href="' . $_SERVER["PHP_SELF"] . '?logout=true">Logout</a></li>
        </ul>';
    }
?>

バグは、ログアウトしてもページが同じままであるということです。つまり、ログインフォームは表示されませんが、同じログアウトとユーザーが表示されます。ページを更新すると、正常になります。

4

10 に答える 10

7

ログアウトすると、最初にCookieの破棄(応答が送信された後に発生します)をキューに入れ、次にページをレンダリングした直後にキューに入れます。ブラウザはレンダリング前にCookieを削除する機会がなく、$_SESSION変数は引き続き有効です。

PHPのドキュメントによるとsession_destroy

session_destroy()は、現在のセッションに関連付けられているすべてのデータを破棄します。セッションに関連付けられているグローバル変数の設定を解除したり、セッションCookieの設定を解除したりすることはありません。

解決策は、セッションとCookieを破棄する代わりに、認証の原因となる変数の設定を解除することです。

unset($_SESSION['user']);
unset($_SESSION['lastActive']);
unset($_SESSION['fingerprint']);

注:コードを関数に分割することをお勧めします。これにより、より整理されて読みやすくなります(そして、正しく実行すれば再利用できます)。

于 2009-01-25T22:01:29.220 に答える
2

$matchingUser['inactive']を使用してデータベースから実際のデータを取得しないため、設定されることはありませんmysql_fetch_assoc()

変更されたバージョン:

$matchingUser = mysql_query("SELECT * FROM `users` WHERE username='$user' AND password=MD5('$password') LIMIT 1");
if (mysql_num_rows($matchingUser))
{
    $matchingUserData = mysql_fetch_assoc($matchingUser);
    if($matchingUserData['inactive'] == 1) //Checks if the inactive field of the user is set to one
    {
        $error = "Your e-mail Id has not been verified. Check your mail to verify your e-mail Id. However you'll be logged in to site with less privileges.";
        $_SESSION['inactive'] = true;
    }
于 2009-01-25T22:46:12.407 に答える
2

セキュリティに関する注意事項:

if($matchingUser['inactive'] == 1)

のように書かれたほうがよい

if(!$matchingUser['inactive'])

データベース スキーマが変更された場合 (たとえば、特定の種類のアクティビティを示す整数になった場合 (私の意見では、これは悪い設計です: 列挙型の方が適切です))、コードに問題が発生するためです。

もちろん、これは二重否定なので、読みにくいかもしれません。より良いでしょう:

if($matchingUser['isactive'])

あるいは:

if($matchingUser->isActive())

User クラスなどを作成すると仮定します。

念のため、必要に応じて or を使用してrequireください(関数宣言が含まれているrequire_once場合は後者が望ましい)。connect.php

ユーザー名の代わりにユーザーの ID をセッション変数に格納します。ユーザーが後で自分の名前を変更できるようにする可能性があり、セッション データは無効になります (少なくとも['user']いずれにせよ)。また、ID (主キー、一意) でデータベース レコードを検索する方が、ユーザー名 (おそらくインデックス付き、文字列) で検索するよりも高速です。

30分後に私を蹴るのは本当に迷惑です. 私がアクセスするのはあなただけではありません。仕事をした後 (たとえば、何かをするために呼び出されたり、昼休みを取ったりした場合) に後で再アクセスすることがあります。

htmlspecialcharsXSS を防止するために使用します。

ここで使用する必要はありません$_SERVER['PHP_SELF']:

<a href="' . $_SERVER["PHP_SELF"] . '?logout=true">

それなしで単に書く:

<a href="?logout=true">

ユーザーが何かを POST するときは、必ずリダイレクトしてください (TODO: 方法に注意してください)。そうしないと、ユーザーの戻るボタンによってデータの再 POST が発生する可能性があります (これはおそらく望ましくないことです!)。

于 2009-01-25T22:14:04.920 に答える
1

MD5はパスワードを暗号化するための非常に弱い手段であり、それを回避する方法はたくさんあります。それは役に立ちますが、IPアドレスを使用してセットアップすることはできますが、IPは確かにかなり変化します。

また、ユーザーのパスワードを破るために誰かが何度もシステムにアクセスしないようにするために、そこには何もありません。

G-マン

于 2009-01-25T21:14:46.710 に答える
1

MD5またはsha1と一緒に塩を使用します。パスワードを生成し、ログイン時にパスワードを確認するために使用するもの。

function generateHash($plainText, $salt = null)
{
    define('SALT_LENGTH', 9);
    if ($salt === null)
    {
        $salt = substr(md5(uniqid(rand(), true)), 0, SALT_LENGTH);
    return array($salt, sha1($salt . $plainText) );
    }
    else
    {
        $salt = substr($salt, 0, SALT_LENGTH);
    return sha1($salt . $plainText);
    }

}

$ plainTextの場合、パスワード変数を送信します。

新しいハッシュを生成する場合、2つの値が返されます。最初の値は「salt」と呼ばれ、2番目の値は暗号化されたパスワードです。それらの両方をデータベースに保存します。

誰かがあなたのサイトにログインしようとし、あなたがチェックしたいとき、彼らのパスワードとsalt変数を関数に送信すると、ハッシュが返されます。次に、それをデータベースに保存されている値と比較して、ユーザーが入力したパスワードが正しいかどうかを確認できます。

于 2009-01-25T21:46:00.627 に答える
1

$_SERVER['REMOTE_ADDR']ユーザーがプロキシの背後にいる場合は変更される可能性があります。

于 2009-01-25T20:50:59.393 に答える
0

Nate Abeleによって説明されたこの方法を使用して、指紋の作成に使用される情報の信頼性を向上させることができます。

于 2009-01-25T21:20:32.533 に答える
0

見栄えがします。setcookie(session_name(), '', time()-3600, '/')基本的に、時刻を現在の時刻より前に設定することにより、Cookieを削除します。

于 2009-01-25T20:52:56.817 に答える
0

setcookie(session_name()、''、time()-3600、'/');

空の文字列を設定し、過去の有効期限を設定することで、古いセッションCookieを消去しているように見えます。ただし、作成者が両方の方法を使用した理由はわかりません。

于 2009-01-25T20:55:11.103 に答える
0

この場合、が正しく機能するためには、サーバー設定が正しいことを確認する必要がありますmysql_real_escape_string。この場合、コードはMagic Quotesがオフになっていることを前提としていますが、多くのサーバーで有効になっていることがよくあります。get_magic_quotes_gpc()サーバー設定が自動的に文字列をエスケープしているかどうかを確認するために、trueまたはfalseを返すかどうかを確認する必要があります。

コードは安全に見えますが、MySQLクエリを実行する新しい方法であるPDOを検討することをお勧めします。これにより、パラメーター化されたクエリが可能になります。

于 2009-01-25T20:59:57.520 に答える