这段代码安全吗?

时间:2009-01-25 20:49:28

标签: php security session login session-variables

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

错误是当我退出时,页面保持不变,即登录表单没有显示,但显示相同的注销和用户。当我刷新页面时,它变得正常。

10 个答案:

答案 0 :(得分:7)

当您注销时,首先,您排队销毁cookie(它将在发送响应后发生),然后立即呈现您的页面。在呈现之前,浏览器没有机会删除cookie,并且您的$_SESSION变量仍然存在。

PHP文档说session_destroy

  

session_destroy()会销毁与当前会话关联的所有数据。它不会取消设置与会话关联的任何全局变量,也不会取消设置会话cookie。

解决方案是,不要破坏会话和cookie,只需取消设置导致身份验证的变量:

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

请注意:我建议将代码拆分为函数。这将使它更有条理和可读(如果你做对了就可以重复使用)。

答案 1 :(得分:2)

一些安全说明:

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

最好写成

if(!$matchingUser['inactive'])

因为如果数据库模式发生了变化(例如,它现在是一个整数来表示某种类型的活动(这是糟糕的设计,在我看来:枚举会做得更好))你的代码会有问题。

当然,这是双重否定,可能不太可读。更好的是:

if($matchingUser['isactive'])

甚至:

if($matchingUser->isActive())

假设您创建了User类等等。

以防万一,必要时使用或requirerequire_once(如果connect.php包含函数声明,最好使用后者)。

将用户ID存储在会话变量中而不是用户名中。有可能您将允许用户稍后更改其名称,并且会话数据将无效(无论如何至少['user'])。通过ID(主键,唯一)查找数据库记录比通过用户名(可能是索引,字符串)更快。

30分钟后踢我真的很烦人。你不是我去过的唯一一个网站,我可能会在做完一些工作之后再去看看(例如,如果我被要求做某事,或者午休时间)。

使用htmlspecialchars来帮助阻止XSS。

此处无需使用$_SERVER['PHP_SELF']

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

简单地在没有它的情况下写作:

<a href="?logout=true">

当用户发布某些内容时,请务必重定向它们(TODO:注意如何)。否则,用户的后退按钮可能会导致重新发布数据(可能不是您想要的!)。

答案 2 :(得分: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;
    }

答案 3 :(得分:1)

如果用户在代理后面,

$_SERVER['REMOTE_ADDR']可能会改变。

答案 4 :(得分:1)

MD5是加密密码的一种非常弱的方法,有很多方法可以解决它。它确实有帮助,但你已经设置了IP地址,但IP肯定会发生很大变化。

此外,您没有任何内容可以确保有人不会一遍又一遍地破坏系统以破坏用户的密码。

G-曼

答案 5 :(得分: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”,第二个值是加密密码。将它们都存储到数据库中。

如果有人尝试登录您的网站,并且您想要检查,请将其密码和salt变量发送给该函数,它将返回一个哈希值。然后,您可以将其与存储在数据库中的值进行比较,以检查用户输入的密码是否正确。

答案 6 :(得分:0)

看起来很好。 setcookie(session_name(), '', time()-3600, '/')基本上通过将时间设置为当前时间之前删除cookie。

答案 7 :(得分:0)

  

setcookie是什么(session_name(),'',time() - 3600,'/');

看起来它会删除旧的会话cookie,通过设置一个空字符串并设置过去的过期时间。但不确定为什么作者使用了这两种方法。

答案 8 :(得分:0)

在这种情况下,您必须确保您的服务器设置正确,mysql_real_escape_string才能正常工作。在这种情况下,您的代码假设Magic Quotes已关闭,而我经常发现许多服务器已启用它。您应该检查get_magic_quotes_gpc()是否返回true或false,以查看服务器设置是否为您自动转义字符串。

代码看起来很安全,但我建议您研究一种新的MySQL查询方式:PDO。这允许参数化查询。

答案 9 :(得分:0)

您可以使用this method described by Nate Abele来提高用于构建指纹的信息的可靠性。