【问题标题】:Is this code secure?这段代码安全吗?
【发布时间】:2009-01-26 10:08:41
【问题描述】:
<?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>';
    }
?>

错误是当我注销时,页面保持不变,即登录表单不显示,但显示相同的注销和用户。刷新页面后就正常了。

【问题讨论】:

  • +1:没有理由对合法问题投反对票
  • == 1)//检查用户的非活动字段是否设置为1
  • 对不起 Pim,我只是想告诉读者我有一个 inactive 字段,如果用户处于非活动状态,它存储值 1。如果你受伤了,对不起

标签: php security session login session-variables


【解决方案1】:

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

PHP 文档说 session_destroy:

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

一种解决方案是不破坏会话和 cookie,而是简单地取消设置会导致身份验证的变量:

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

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

【讨论】:

  • 啊酷,这为我解决了一些问题。我想知道为什么 session_destroy() 没有为我做任何事情,所以最终像你说的那样取消了所有会话变量。
【解决方案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:注意如何)。否则,用户的后退按钮可能会导致数据重新 POST(这可能不是您想要的!)。

【讨论】:

  • 谢谢,你的回答很有用
【解决方案3】:

$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;
    }

【讨论】:

    【解决方案4】:

    如果用户在代理后面,$_SERVER['REMOTE_ADDR'] 可能会发生变化。

    【讨论】:

      【解决方案5】:

      MD5 是一种非常弱的密码加密方法,有很多方法可以绕过它。它确实有帮助,但是您可以使用 IP 地址设置它,但 IP 肯定会发生很大变化。

      此外,您没有任何东西可以确保有人不会一遍又一遍地访问系统来破解用户的密码。

      G-Man

      【讨论】:

      • 为什么是负数?这是系统中的一个有效安全漏洞,这正是 OP 所要求的。
      【解决方案6】:

      将盐与 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 变量发送给函数,它将返回一个哈希值。然后你可以将它与数据库中存储的值进行比较,以检查用户输入的密码是否正确。

      【讨论】:

      • 这将在每次调用函数时定义常量。
      • 盐应该每次都是唯一的。通常它被称为随机数,就是这样:一个一次性的词。
      【解决方案7】:

      看起来不错。 setcookie(session_name(), '', time()-3600, '/') 实质上是通过将时间设置为当前时间之前来删除 cookie。

      【讨论】:

      • 当浏览器看到一个过期日期已经过去的cookie时,它会删除这个cookie。
      • 好的。为什么要删除以前从未创建过的 cookie?
      • iSattar,cookie 已由会话自动创建。当您执行 session_Start() 时,如果不存在,它会创建一个 cookie。
      【解决方案8】:

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

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

      【讨论】:

        【解决方案9】:

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

        【讨论】:

          【解决方案10】:

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-10-29
            • 2013-02-20
            • 1970-01-01
            • 2023-04-10
            • 2011-05-18
            相关资源
            最近更新 更多