我的PHP登录系统有多安全?

我的PHP登录系统有多安全?,php,security,authentication,encryption,login,Php,Security,Authentication,Encryption,Login,我是PHP新手,这也是我第一次登录系统,所以如果你们能查看我的代码,看看是否能发现任何安全漏洞,那就太好了: 注意:我正在清理所有用户输入,尽管这里没有显示 注册: 步骤1:我获取用户选择的密码,并通过此函数运行它: encrypt($user_chosen_password, $salt); function encrypt($plain_text, $salt) { if(!$salt) { $salt = uniqid(rand(0, 1000000));

我是PHP新手,这也是我第一次登录系统,所以如果你们能查看我的代码,看看是否能发现任何安全漏洞,那就太好了:

注意:我正在清理所有用户输入,尽管这里没有显示

注册: 步骤1:我获取用户选择的密码,并通过此函数运行它:

encrypt($user_chosen_password, $salt);

function encrypt($plain_text, $salt) {
    if(!$salt) {
        $salt = uniqid(rand(0, 1000000));
    }
    return array(
        'hash' => $salt.hash('sha512', $salt.$plain_text),
        'salt' => $salt
    );
}
步骤2:然后我将哈希和salt(
$password['hash']
$password['salt']
)存储在数据库的用户表中:

id | username | password  | salt       | unrelated info...
-----------------------------------------------------------
1  | bobby    | 809a28377 | 809a28377f | ...
                fd131e5934
                180dc24e15
                bbe5f8be77
                371623ce36
                4d5b851e46
encrypt($user_entered_password, $salt);
user_id | key
------------------------------------------------------------
1       | 431b5f80879068b304db1880d8b1fa7805c63dde5d3dd05a5b
登录: 步骤1:我获取用户输入的用户名,并在数据库中查找是否返回了任何行。在我的网站上,没有两个用户可以共享相同的内容 用户名,因此用户名字段始终具有唯一值。如果我返回1行,我会为该用户抓取盐

第2步:然后我通过加密功能运行用户输入的密码(如上所述),但这次我还提供了从数据库检索到的salt:

id | username | password  | salt       | unrelated info...
-----------------------------------------------------------
1  | bobby    | 809a28377 | 809a28377f | ...
                fd131e5934
                180dc24e15
                bbe5f8be77
                371623ce36
                4d5b851e46
encrypt($user_entered_password, $salt);
user_id | key
------------------------------------------------------------
1       | 431b5f80879068b304db1880d8b1fa7805c63dde5d3dd05a5b
步骤3:我现在有了与此变量匹配的正确密码:
$password['hash']
。因此,我对数据库进行了第二次查找,以查看 输入的用户名和散列密码一起返回一行。如果是,则用户的凭据是正确的

步骤4:为了在用户的凭据通过后登录,我生成一个随机唯一字符串并对其进行散列:

$random_string = uniqid(rand(0, 1000000));
$session_key = hash('sha512', $random_string);
然后,我将
$session\u键插入数据库中的
活动会话
表:

id | username | password  | salt       | unrelated info...
-----------------------------------------------------------
1  | bobby    | 809a28377 | 809a28377f | ...
                fd131e5934
                180dc24e15
                bbe5f8be77
                371623ce36
                4d5b851e46
encrypt($user_entered_password, $salt);
user_id | key
------------------------------------------------------------
1       | 431b5f80879068b304db1880d8b1fa7805c63dde5d3dd05a5b
第五步:

我将在最后一步中生成的未加密的唯一字符串(
$random\u string
)设置为cookie的值,我称之为
活动\u会话

setcookie('active_session', $random_string, time()+3600*48, '/');
第6步:

在my
header.php
include的顶部有以下检查:

if(isset($_COOKIE['active_session']) && !isset($_SESSION['userinfo'])) {
   get_userinfo();
}
get_userinfo()
函数查找数据库中的
users
表,并返回一个关联数组,该数组存储在名为
userinfo
的会话中:

    $_SESSION['userinfo'] = array(
        'user_id'           => $row->user_id,
        'username'          => $row->username,
        'dob'               => $row->dob,
        'country'           => $row->country,
        'city'              => $row->city,
        'zip'               => $row->zip,
        'email'             => $row->email,
        'avatar'            => $row->avatar,
        'account_status'    => $row->account_status,
        'timestamp'         => $row->timestamp,
    ); 
//首先,此函数获取活动会话cookie的值并对其进行哈希运算以获取会话密钥:

hash('sha512', $random_string);
//然后,它在
活动会话
表上进行查找,查看此
的记录是否存在,如果存在,它将获取与该记录关联的
用户id
,并使用该id在
用户
表上进行第二次查找,以获取
用户信息

    $_SESSION['userinfo'] = array(
        'user_id'           => $row->user_id,
        'username'          => $row->username,
        'dob'               => $row->dob,
        'country'           => $row->country,
        'city'              => $row->city,
        'zip'               => $row->zip,
        'email'             => $row->email,
        'avatar'            => $row->avatar,
        'account_status'    => $row->account_status,
        'timestamp'         => $row->timestamp,
    ); 
如果
userinfo
会话存在,我知道该用户已通过身份验证。如果它不存在,但
活动会话
cookie存在,则检查 php
文件的顶部将创建该会话

我之所以使用cookie而不是单独使用会话,是为了持久化登录。因此,如果用户关闭浏览器,会话可能会消失,但 cookie仍然存在。由于在
header.php
的顶部有一个检查,会话将被重新创建,用户可以作为一个登录用户使用 在用户中,一切正常

注销: 步骤1:未设置
userinfo
会话和
活动会话
cookie

步骤2:删除数据库中
活动会话
表中的相关记录


注意:我能看到的唯一问题(可能还有很多其他问题)是,如果用户通过在浏览器中自己创建
活动会话
cookie来伪造该cookie。当然,他们必须设置一个字符串作为cookie的值,该字符串在加密后必须与
活动会话
表中的记录匹配,我将从该表中检索
用户id
,以创建该会话。我不确定实际情况下,用户(可能使用自动程序)正确猜测他们不知道的字符串,然后将对其进行加密,并与数据库中
active\u sessions
表中的字符串进行匹配,以获取用于构建该会话的用户id

很抱歉,这篇文章太大了,但因为这是我网站的关键部分,而且由于我缺乏经验,我只想让更有经验的开发人员来运行它,以确保它实际上是安全的

那么,您是否发现这条路线存在任何安全漏洞,以及如何改进

…通过加密功能运行用户输入的密码


那么,密码是如何从浏览器传输到服务器的呢?您没有提到防止中间人攻击。

您应该包括某种超时或故障切换,以防止暴力攻击。有很多方法可以做到这一点,包括基于IP的阻塞、增量超时等。这些方法都无法阻止黑客,但它们会让黑客变得更加困难

另一点(你没有提到,所以我不知道你的计划)是失败消息。使故障信息尽可能模糊。提供类似“该用户名存在,但密码不匹配”的错误消息可能会对最终用户有所帮助,但会破坏登录功能。您刚刚转换了一个蛮力攻击,该攻击需要
O(n^2)
时间才能
O(n)
+
O(n)
。黑客不需要尝试彩虹表中的每个排列(例如),而是首先尝试用户名的所有值(使用设置的密码),直到失败消息更改。然后,它知道一个有效的用户,只需强制输入密码

按照这些思路,您还应该确保用户名存在和不存在的时间相同。当用户名实际存在时,您正在运行其他进程。因此,用户名存在时的响应时间比用户名不存在时的响应时间要长。一个技术高超的黑客可以对页面请求进行计时,以找到一个有效的用户名

同样,您应该确保,除了过期的cookie之外,您