这个对用户登录和验证是否足够安全?(PHP,MySQL,会话)

3
我想确认是否有什么显而易见的漏洞。需要您们专业知识的帮助。
数据库中的用户数据: - 密码存储为通过crypt()加盐处理的字符串,盐值存储在用户表中。
会话: - 当用户成功登录后,我会在sessions表中创建一个具有唯一sha256哈希会话ID的新行。我还在其中存储了user_id以便查看哪个会话与哪个用户相关。 - 我创建了一个cookie,并将会话ID存储在其中。 - 通过访问登出功能,可以删除会话。 - 会话在定义的过期时间(例如7天)后自动删除。
认证: - 检查客户端是否有一个包含会话ID的cookie。如果cookie中的会话ID与sessions表中的某个会话ID相匹配,则用户已经通过身份验证。
你们觉得如何?还需要做些什么吗?
编辑:添加了生成密码的方法:
public function generate_salt($password) {
    $cost = $this->CONFIG['user__password_encrypt_cost'];
    $salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');

    // Prefix information about the hash so PHP knows how to verify it later.
    // "$2a$" Means we're using the Blowfish algorithm. The following two digits are the cost parameter.
    $salt = sprintf("$2a$%02d$", $cost) . $salt;

    // Hash the password with the salt
    $hash = crypt($password, $salt);


    return array(
        'salted_password' => $hash,
        'salt' => $salt
     );
}

谢谢!我已经标记为已解决 :) - Nick
2个回答

1

使用CRYPT_SHA512和盐值进行加密是安全的,但建议使用password_hash()。

不应使用使用MD5或类似的弱哈希算法进行加密。

会话处理过于复杂。

PHP具有会话处理功能,您可以将cookie存储在会话中(实际上它会自动存储)。在会话中,您可以添加用户ID。

$_SESSION['userid'] = $sUserIdFromDB;

如果客户端有cookie,将找到会话数据并且您可以检查是否已登录。
if (isset($_SESSION['userid']) 
    AND is_int($_SESSION['userid']) AND (0 < $_SESSION['userid'])) {
    $oUserRow = $oDB->getByID($_SESSION['userid']);
} else {
   // user is not logged in (has no valid session)
}

将Session ID存储在数据库中是可行的,但会增加不必要的额外请求到数据库。这并不会增加安全性。实际上,如果用户同时从两个不同的浏览器(移动设备和桌面电脑、移动API)登录,使用数据库方法会出现问题,因为Session ID将是不同的。
更重要的是: - 每个用户应该有一个不同的盐 - 强制密码最小长度(8个字符) - 强制使用大量字符(小写字母、大写字母、数字、特殊字符) - 允许安全地重置新密码(不应通过电子邮件发送密码) - ....

谢谢!将会话存储在表中的原因是我想要能够查看在线用户以及他们最后执行的操作。因此,对于每个页面加载,我都会使用该信息更新会话表中的信息。我还将拥有一个定义用户从哪个客户端登录的列,以便我可以区分这些信息,并向用户显示他正在使用的前端等等... - Nick
非常感谢您提供这个信息,我刚编码进入那个(数据库问题)并且很快就发现了——这正是发生的事情.. 不匹配和无效会话。不去管它,让PHP处理它——generate_session_id()并将其添加到数组$_SESSION变量中是最好的方式,并完美地处理一切。我非常感激,这解释了为什么我们的开发人员更新了我们的登录类并将存储会话ID的旧方式弃用到单独的数据库表中。当时我们有很多问题。干杯! - WiiLF
@Nick 我确实这样做了,就像我们以前的实现方式一样,它确实可以工作,但是你将永远不会看到超过会话过期时间的用户更新 - 这意味着如果登录时间设置为7天,则您将在7天内不会看到该特定用户的更新(例如,他们可能在仅3天内登录了11次)。最好根据用户表中的“last_login”字段或类似字段进行操作,并读取该日期时间,因为这是最有效的方法。 - WiiLF

1
"足够安全"这个问题有点难回答,但以下是需要考虑的其他事项:
  1. 盐强度——它是用户名还是随机值或时间戳?最好选择后两者。
  2. 在密码通过网络发送时,是否要求 SSL?考虑执行此操作。
  3. Cookie 是否设置为“Secure”和“HttpOnly”,以减轻 CSRF 的影响?如果可以,请执行此操作(取决于客户端支持)。
  4. 员工是否被限制访问盐和加密值?限制谁可以访问任何敏感信息。
  5. 使用哪种加密算法以及多少位?尝试 DES 512 或更强。
  6. 是否强制实施密码强度要求?您应该这样做!
  7. 数据库连接是否安全?特别重要的是,如果 DB 链接在广域网上。

  1. 请查看我的原始问题帖子,我已更新了用于密码生成的代码。
  2. 实际上,整个网站都需要 SSL,至少这是我的计划。
  3. 谢谢,我会研究一下!我之前不知道,现在只是这样的: setcookie('cookie_name', $session_id, $expire);
  4. 您是指对数据库进行物理限制访问吗?是的。
  5. 请参见我以上的密码生成代码。
  6. 是的。
  7. 数据库连接由 PHP 连接到位于 Web 根目录外的后端。没有前端可见性。
再次感谢!
- Nick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接