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

TK1*_*123 40 php security authentication encryption login

我是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
    );
}
Run Code Online (Sandbox Code Playgroud)

第2步:然后我将哈希和salt($password['hash']$password['salt'])存储在数据库的users表中:

id | username | password  | salt       | unrelated info...
-----------------------------------------------------------
1  | bobby    | 809a28377 | 809a28377f | ...
                fd131e5934
                180dc24e15
                bbe5f8be77
                371623ce36
                4d5b851e46
Run Code Online (Sandbox Code Playgroud)

登录:

步骤1:我获取用户输入的用户名并查看数据库以查看是否返回了任何行.在我的网站上,没有2个用户可以共享相同的用户名,因此用户名字段始终具有唯一值.如果我返回1行,我会抓住该用户的盐.

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

encrypt($user_entered_password, $salt);
Run Code Online (Sandbox Code Playgroud)

第3步:我现在有一个正确的密码来匹配这个变量:$password['hash'].所以我在数据库上进行第二次查找,看看输入的用户名和散列密码是否一起返回一行.如果是,那么用户的凭证是正确的.

步骤4:为了在用户凭证通过后记录用户,我生成一个随机唯一字符串并对其进行哈希处理:

$random_string = uniqid(rand(0, 1000000));
$session_key = hash('sha512', $random_string);
Run Code Online (Sandbox Code Playgroud)

然后我插入数据库中$session_keyactive_sessions表:

user_id | key
------------------------------------------------------------
1       | 431b5f80879068b304db1880d8b1fa7805c63dde5d3dd05a5b
Run Code Online (Sandbox Code Playgroud)

第5步:

我接受在最后一步($random_string)中生成的未加密的唯一字符串,并将其设置为我调用的cookie的值active_session:

setcookie('active_session', $random_string, time()+3600*48, '/');
Run Code Online (Sandbox Code Playgroud)

第6步:

在我的header.php包括顶部有这个检查:

if(isset($_COOKIE['active_session']) && !isset($_SESSION['userinfo'])) {
   get_userinfo();
}
Run Code Online (Sandbox Code Playgroud)

get_userinfo()函数users对数据库中的表执行查找,并返回一个关联数组,该数组存储在名为的会话中userinfo:

//首先此函数获取active_session cookie的值并对其进行哈希以获取session_key:

hash('sha512', $random_string);
Run Code Online (Sandbox Code Playgroud)

//那么它的查找active_sessions,看看表,如果该记录key存在,如果是的话它会抢了user_id与该记录相关联,并使用该做的第二个查找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,
    ); 
Run Code Online (Sandbox Code Playgroud)

如果userinfo会话存在,我知道用户已通过身份验证.如果它不存在但active_sessioncookie存在,那么header.php文件顶部的检查将创建该会话.

我之所以使用cookie而不是单独的会话,是为了坚持登录.因此,如果用户关闭浏览器,会话可能会消失,但cookie仍然存在.并且由于在顶部有检查header.php,因此会重新创建会话,并且用户可以像平常一样充当登录用户.

登出:

第1步:无论是userinfo会议和active_session饼干都没有设置.

步骤2:active_sessions删除数据库中表的关联记录.


注意:我可以看到的唯一问题(也许还有许多其他问题)是,如果用户active_session通过在浏览器中自行创建cookie来伪造该cookie.当然,他们必须将cookie的值设置为一个字符串,在加密后,该字符串必须与active_sessions表中的记录匹配,我将从中检索user_id创建该会话的记录.我不确定这是多么现实的可能性,对于一个用户(可能使用自动程序)正确猜测一个他们不知道的字符串然后将被sha512加密并与active_sessions数据库中的表中的字符串匹配到获取用户ID以构建该会话.

对于这篇大文章感到抱歉,但由于这是我网站的一个关键部分,由于我的经验不足,我只想让更有经验的开发人员运行它,以确保它真的安全.

所以你看到这条路线上有任何安全漏洞,如何改进?

set*_*rgo 29

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

另一点(你没有提到,所以我不知道你的计划)是失败的消息.使失败消息尽可能模糊.提供一个错误信息,如"该用户名存在,但密码不匹配"可能有助于最终用户,但它杀死登录功能.你刚刚转换了一个需要花费O(n^2)时间O(n)+ 的蛮力攻击O(n).黑客只需先尝试用户名(使用设置密码)的所有值,直到失败消息发生变化,而不是需要尝试彩虹表中的每个排列(例如).然后,它知道一个有效的用户,只需强制密码.

除此之外,您还应确保在用户名存在且不存在时经过相同的时间.当用户名实际存在时,您正在运行其他进程.因此,当用户名存在时,响应时间会更长,而不存在.一个非常熟练的黑客可以定时页面请求以找到有效的用户名.

同样,除了过期的cookie之外,还应该确保会使会话表到期.

最后,在get_user_info()调用中,如果存在多个并发的活动登录,则应终止所有打开的会话.确保在一定量的不活动(例如30分钟)后超时会话.

按照@Greg Hewgill提到的内容,您没有包含以下任何内容:

  • Server-Client之间的SSL /加密连接
  • 您经常用于处理身份验证的其他传输协议(如OAuth)

您的服务器是安全的,但如果有人可以读取已交换的数据(MITM),那么算法的安全性并不重要.您应该确保只通过加密协议进行通信.

  • 我讨厌*当一个网站告诉我"密码与电子邮件地址不匹配"时,当我尝试一个荒谬的电子邮件时,"用户不存在".并且...当网站*给我发电子邮件*密码我"忘记了".谢谢.我已经看到据称精通风险的网站会这样做,并且拒绝承认为什么这样做会有问题. (2认同)

Gre*_*ill 9

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

那么密码如何从浏览器传到服务器?你没有提到防范中间人攻击.

  • 对,那是正确的.如果我能看到浏览器和服务器之间的数据包(有很多方法),那么我可以看到用户的密码以明文形式出现. (2认同)

Sco*_*ski 6

此代码...

function encrypt($plain_text, $salt) {
    if(!$salt) {
        $salt = uniqid(rand(0, 1000000));
    }
    return array(
        'hash' => $salt.hash('sha512', $salt.$plain_text),
        'salt' => $salt
    );
}
Run Code Online (Sandbox Code Playgroud)

...不好。使用新的密码API并完成操作。除非您是专家,否则您不应尝试设计自己的身份验证系统。他们很难做到正确

为了在通过凭据后登录用户,我生成了一个随机的唯一字符串并将其哈希:

只要让PHP处理会话管理即可rand()并且mt_rand()都是非常不安全的随机数生成器。