首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >这个php/mysql登录系统安全吗?

这个php/mysql登录系统安全吗?
EN

Stack Overflow用户
提问于 2014-12-21 16:10:45
回答 4查看 1.1K关注 0票数 0

我的登录系统不采用通常的SELECT * FROM login WHERE user='$user' AND pass='$pass'和计数行的方法,而是从数据库中提取密码,并根据输入的密码进行检查。我试着用'or 1=1来对付它,但没有奏效。所以我在这件事上有什么不安全感:

代码语言:javascript
复制
<?php
        $con = mysqli_connect("","root","mypassword","logins"); //Connect to MySql DB, in format host,user,password,db_name
        $query = mysqli_query($con,"SELECT Pass from login WHERE User='" . $_REQUEST["username"] . "'"); //Query that selects the password if the user equals the request string for "username"
        $row = mysqli_fetch_array($query); //Get the result in array form from the query
        if($_REQUEST["password"] == $row[0] && $row[0] != null){ /* Check if Password in the database equals the request string for the password */
                echo 'Login Sucsessful!';
        }
        else{
                echo 'Login failed!';
        }
?>

我的另一个问题是,为什么大多数人都喜欢SELECT * FROM login WHERE user='$user' AND pass='$pass'方法,而不是这个。还有,这有什么不安全的地方吗?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-12-21 17:20:14

密码加密方案

关于使用纯文本密码,请参阅其他答案。这不是个好选择。BCrypt (在PHP中随时可用)是一条可行的道路。

要真正疑神疑鬼,你应该使它,如果用户不在数据库中,一个假用户被恢复一个肯定错误的密码(例如浏览器用户提供的密码,加上一个字母)。然后继续运行密码检查。这将防止设计用于找出系统中的用户(更长的检查时间)和不存在的用户(快速、立即失败)的定时攻击,方法是强制所有用户都有更长的检查时间,而不管他们是否存在。

可开发性

通常,在不检查/转义的情况下接受用户输入的必须被视为始终不安全的

当然,在您的情况下,1=1漏洞不起作用,分号漏洞对MySQLi无害。

但是针对用户的注入如何,并在文章中提供如下内容:

代码语言:javascript
复制
username=foo' limit 0 UNION select 'pwn3d!' AS Pass;--
password=pwn3d!

现在,查询变成:

代码语言:javascript
复制
SELECT Pass from login WHERE User='foo' limit 0 UNION select 'pwn3d!' AS Pass;--';

或者,如果有人想避免使用--;,理由是某些SQL分析可能会将查询拒绝为不健全的查询(例如,因为这是一个不常见或不受支持的多个查询,而且无论如何都是可疑的),那么这个更复杂的username

代码语言:javascript
复制
foo' LIMIT 0 UNION SELECT 'pwn3d!' AS Pass UNION SELECT Pass FROM login WHERE ''!='

这将产生:

代码语言:javascript
复制
+--------+
| Pass   |
+--------+
| pwn3d! |
+--------+
1 row in set (0.00 sec)

当然,由于我们选择了密码,这总是等于我们提供的密码。

,因此登录检查将通过

这就是小提琴 (删除SQLfiddle似乎不喜欢的SQL注释)。

这要求攻击者知道密码字段的名称,而在实际实现中,此攻击(如上所述)将无法工作(用户信息丢失)。但是,根据实际实现,可以调整它并以任何用户的身份实际登录,前提是您知道他/她的登录名,甚至不知道密码是什么。

另一个有趣的可能性是将用户名的MD5哈希与用户名一起存储。那么查询可能是

代码语言:javascript
复制
SELECT * FROM (
    SELECT * FROM login WHERE hashed_username=?
    UNION
    SELECT [list of default fields], ? AS Pass
) LIMIT 1;

它总是返回一个记录,不管用户是否存在;hashed_username提供的值是md5($_REQUEST['username']),即使我们没有使用准备好的查询,它也可以防止任何SQL攻击,并且带有hashed_username索引可以保证定时攻击是不可行的。默认密码提供的值是BCrypt::hash("_NOT_".$_REQUEST['password'])

此时,我们只运行BCrypt::verify($record['Pass'], $_REQUEST['password']),看看它是返回true (用户存在,密码确定)还是false (坏用户还是坏密码)。这两条执行路径现在已经足够接近,以至于没有合理数量的强暴行为可以导致攻击者发现有效的用户名或密码。一个合适的BCrypt round值还可以保证强暴受损的数据库实际上是不可行的。

此时,易受攻击的点是密码强度。请记住,一个非常复杂的密码策略会让用户发现将其写下来并将其录制到监视器上是实用的或必要的,这实际上对安全性是有害的(曾经这样做过)。(叹息)。

票数 4
EN

Stack Overflow用户

发布于 2014-12-21 17:07:10

您不应该将密码存储为纯文本。这样做可能会暴露您的所有用户的信息,以防您的数据库被破坏。由于许多人在多个服务中使用相同的密码,这可能是一个巨大的安全问题。

您应该按照注释中的建议查看密码散列。

您还容易受到SQL注入的影响。不要在查询中直接使用用户提供的信息,因为可以在username字段中写入可能对数据库执行各种操作的特定字符串。

要解决这个问题,请查看准备好的语句。

票数 1
EN

Stack Overflow用户

发布于 2014-12-21 17:41:16

为了展示登录脚本的安全性(在大多数情况下),我从这个项目中获取了一些类的部分内容:php-登录

下面是如何在数据库中存储密码:

代码语言:javascript
复制
// crypt the user's password with the PHP 5.5's password_hash() function, results in a 60 character hash string
// the PASSWORD_DEFAULT constant is defined by the PHP 5.5, or if you are using PHP 5.3/5.4, by the password hashing
// compatibility library. the third parameter looks a little bit shitty, but that's how those PHP 5.5 functions
// want the parameter: as an array with, currently only used with 'cost' => XX.

$user_password_hash = password_hash($user_password, PASSWORD_DEFAULT, array('cost' => $hash_cost_factor));



// write new users data into database using PDO and of course prepared statements

 $query_new_user_insert = $this->db_connection->prepare('INSERT INTO users (user_name, user_password_hash, user_email, user_activation_hash, user_registration_ip, user_registration_datetime) VALUES(:user_name, :user_password_hash, :user_email, :user_activation_hash, :user_registration_ip, now())');
$query_new_user_insert->bindValue(':user_name', $user_name, PDO::PARAM_STR);
$query_new_user_insert->bindValue(':user_password_hash', $user_password_hash, PDO::PARAM_STR);
$query_new_user_insert->bindValue(':user_email', $user_email, PDO::PARAM_STR);
$query_new_user_insert->bindValue(':user_registration_ip', $_SERVER['REMOTE_ADDR'], PDO::PARAM_STR);
$query_new_user_insert->execute();
// id of new user
$user_id = $this->db_connection->lastInsertId();

请注意,我们只插入了密码的散列表单(密码),而不是密码本身->->您不应该这样做!请注意,如果用户名或电子邮件已经被使用,请注意检查。

您应该只插入密码的哈希值。您不应该插入未散列的密码

现在,如果我们想要登录一个用户,我们将使用密码并再次使用准备好的语句:

代码语言:javascript
复制
$query_user = $this->db_connection->prepare('SELECT * FROM users WHERE user_name = :user_email');
$query_user->bindValue(':user_name', trim($user_name), PDO::PARAM_STR);
$query_user->execute();
// get result row (as an object)
$result_row = $query_user->fetchObject();

// here we will check for the correct password / check if the password hash doesn't fit (mind the ! password_verify):  
if (! password_verify($user_password, $result_row->user_password_hash)) {

// increment the failed login counter for that user
$sth = $this->db_connection->prepare('UPDATE users '
. 'SET user_failed_logins = user_failed_logins+1, user_last_failed_login = :user_last_failed_login '
. 'WHERE user_name = :user_name OR user_email = :user_name');
$sth->execute(array(':user_name' => $user_name, ':user_last_failed_login' => time()));

在这种情况下,我们将回显一条消息或将无法正确识别的用户重定向到登录页面.

现在,事情发生了匹配的部分:

代码语言:javascript
复制
    else {
// write user data into PHP SESSION [a file on your server]
$_SESSION['user_id'] = $result_row->user_id;
$_SESSION['user_name'] = $result_row->user_name;
$_SESSION['user_email'] = $result_row->user_email;
$_SESSION['user_logged_in'] = 1;
// declare user id, set the login status to true
$this->user_id = $result_row->user_id;
$this->user_name = $result_row->user_name;
$this->user_email = $result_row->user_email;
$this->user_is_logged_in = true;
// reset the failed login counter for that user
$sth = $this->db_connection->prepare('UPDATE users '
. 'SET user_failed_logins = 0, user_last_failed_login = NULL '
. 'WHERE user_id = :user_id AND user_failed_logins != 0');
$sth->execute(array(':user_id' => $result_row->user_id));

我们将为用户设置一个$_SESSION,并使用该$_SESSION来标识他/她,您可以在github上的库中看到这一点。您现在也可以重定向用户。

您应该在登录区域内的每个页面加载上运行登录/设置会话的检查。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27591182

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档