首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PHP登录注册系统

PHP登录注册系统
EN

Code Review用户
提问于 2018-05-12 19:02:58
回答 1查看 153关注 0票数 3

我目前正在学习PHP,并决定编写一个登录和注册系统,以了解错误处理、mysql、密码哈希和基本查询。

我知道有一些标准的身份验证系统具有类似Laravel这样的框架,但我想自己编写一个,只是为了学习。

我想得到一些反馈,基于:

  • 安全性
  • 编码效率
    • 不需要的代码
    • 减慢脚本速度的代码
    • 错误地使用函数
    • 不良压痕

安全当然是主要的事情在这里,如果你有任何其他反馈,请张贴它,因为它是非常感谢!

  • SQL注入
  • XSS
  • 会话劫持

My注册表单

代码语言:javascript
复制
prepare('INSERT INTO `users` (user_name, user_password, user_email, user_perms, user_created_at) VALUES (?, ?, ?, ?, ?)');

    if($stmt) {
      // If statement gets sent correctly, create variables and add them
      $username = $_POST['username'];

      // Hash users' password
      $password = password_hash($_POST['password'], PASSWORD_DEFAULT);

      $email = $_POST['email'];

      // Timestamp
      $date = date('Y-m-d H:i:s');

      // Standard user permission level
      $perms = "Gebruiker";

      // Bind parameters
      $stmt->bind_param('sssss', $username, $password, $email, $perms, $date);

      // Check whether query ran succesfully or not
      if($stmt->execute()) {
        $err = "Het account is aangemaakt";
        $class = "succes";
      } else {
        $err = "Deze gebruikersnaam wordt al gebruikt";
        $class = "fail";
      }
    }
  }
}

?>



    
        
    
    
    

    REGISTER

    
    
    
    
        

    
    
    
    
        
            
                
                    REGISTER
                    ";
                            echo "" . $err . "";
                            echo "";
                        }
                    ?>
                    
          
                    
                    
          Ik heb al een account

My登录表单

代码语言:javascript
复制
prepare('SELECT user_name, user_password, user_email, user_perms FROM `users` WHERE user_name = ?');

        if($stmt) {
            $username = $_POST['username'];
            $password = $_POST['password'];

      // Send parameters to the server
            $stmt->bind_param('s', $username);

      // Run statement
            $stmt->execute();

            // Get query result
            $result = $stmt->get_result();

            // Fetch the query result in a row and bind to variables
            while($row = $result->fetch_assoc()) {
                $hash = $row['user_password'];
                $username = $row['user_name'];
                $email = $row['user_email'];
                $userPerms = $row['user_perms'];
            }
            // If there's no hash, the user does not exists
            if(empty($hash)) {
                $err = "Sorry, maar deze gebruik bestaat niet";
                $class = "fail";
            } else {
                // Check whether password matches hash on server
                if(password_verify($password, $hash)) {
                    // Hash matches password
                    session_start();

                    // Bind session variables
                    $_SESSION['username'] = $username;
                    $_SESSION['email'] = $email;

                    // Login succesful, set message and class accordingly
                    $err = "Succesvol ingelogt. Redirecting...";
                    $class = "succes";

                    // Redirect to secured page
                  header("Location: admin.php");
                } else {
                    // Password doesn't match hash, set error message accordingly
                    $err = "Wachtwoord is incorrect";
                    $class = "fail";
                }
            }
        }
    }
}

?>



    
        
    
    
    

    NNNEXT - INLOGGEN

    
    
    
    
        

    
    
    
    
        
            
                
                    
                        Inloggen op nnnext.click
                        ";
                                echo "" . $err . "";
                                echo "";
                            }
                        ?>
                        
                        
                        
                        Ik heb nog geen account

My身份验证文件

代码语言:javascript
复制

My注销文件

代码语言:javascript
复制
EN

回答 1

Code Review用户

回答已采纳

发布于 2018-05-12 22:51:55

我不会进入安全领域(其他人会在这方面做得更好),但是我可以看到一些一般性的问题,或者是一些小问题。

简单干净的代码使许多事情变得更容易。重构、错误发现、维护等。

例如,当您检查某件事情是真的,然后是大量的代码,然后是另一个代码,就很难读懂。尽量尽量保持短小。

例如,您的if ($stmt),而不是检查是否有问题,并提前返回:

代码语言:javascript
复制
if (!$stmt) {
    $error = [
        'error' => 'We are having problems retrieving results. etc'
    ];
    // Then call or return to your view and tell them about the error
}

// No need for an else, just do the code
// as the above would have returned if there was a problem

也就是说,上面的内容应该与声称是登录表单的文件分离。这不是登录表单,即数据库查询和检查查询是否有效。

如果您将文件解耦成不同的部分,则可以重用这些内容。例如,在不同的文件中有多少次连接到数据库,检查连接是否正常,然后执行类似的操作?这可以集中在一个地方,每一个需要使用的地方,都可以称为它设置的一个地方。https://en.wikipedia.org/wiki/Single_责任_原则 https://en.wikipedia.org/wiki/Separation_的_关切

For示例:您的“登录表单”文件应该是从控制器传递数据的表单文件。不是数据库调用和错误检查以及数据检索。

在登录和注册中,包含DB文件并检查一些post数据,如果出现问题,则设置错误和类。他们俩都做同样的事。一个让您思考如何开始将代码解耦以使代码更易于管理的示例:

文件formInputErrors.php (或任何适合的名称):

代码语言:javascript
复制
function formInputErrors(array $formInput = [])
{
    foreach ($formInput as $inputValue) {

        if (empty($inputValue)) {
            return [
                'error' => 'Vul alle velden in',
                'class' => 'fail',
            ];
        }
    }

    return null;
}

然后,在您的登录名注册文件中,只需执行以下操作:

代码语言:javascript
复制
include 'formInputErrors.php';

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $formErrors = formInputErrors($_POST);
    if ($formErrors !== null) {
        // Call or return to your view and tell them about the error
    }
}

检查是集中的,可以在任何地方重复使用,并且是相同的(没有烦人的复制、粘贴或粘贴失败,也没有相同的检查等等)。如果您想要更改它,例如可能所有字段都不需要是数字,那么您可以在一个地方更改它。

在这一点上,你应该检查的不仅仅是空的。这是一个类有不同的方法的地方(一个用于电子邮件检查,一个用于名称,等等)。但是,您可以将更多的函数放在这个单独的文件中,然后调用您需要的任何一个函数。如function checkEmailAddress()等。

在您的“登录表单”代码中:

代码语言:javascript
复制
  $password = password_hash($_POST['password'], PASSWORD_DEFAULT);

您实际上已经定义了如何在脚本中哈希密码,这个脚本应该是用于“登录表单”的。如果需要在其他地方散列密码,则必须在那里复制密码,然后在两个位置定义密码。

如果您需要更改散列密码的方式(不太可能,但规则适用于所有方面,不管多么不可能),那么您需要找出您所做的所有位置并对其进行更改。希望你把它们都拿走了,别留下窃听器或者更糟的,一个小小的安全漏洞.

因此,另一个与上面的解耦示例:

在一个单独的文件中:

代码语言:javascript
复制
function hashPassword($passwordToHash)
{
    return password_hash($passwordToHash, PASSWORD_DEFAULT);
}

然后,您需要知道密码的散列值的任何地方:

代码语言:javascript
复制
$password = hashPassword($_POST['password'];

if ($password === false) {
    return [
        'error' => 'Something went wrong in our system, try again, contact if persist',
    ];
    // Call or return to your view and tell them about the error
}

您使用准备好的语句,这是很棒的,但它不是魔术棒。您不检查用户输入的数据是否“有效”。我可以通过"meh“的电子邮件地址等。

有非标准字符的用户名呢?那么重复的用户名呢?如果字段是唯一的,那么您需要检查它没有被使用,而不是因为重复的插入错误而让MySQL错误退出。

如果您的用户名字段是varchar(25),并且我发送了30个字符,我的用户名可能会被存储,但会被截断,这意味着您允许我用我想要的用户名注册5个字符。也许我没有注意到这一点,然后很难在下一次登录。

你怎么做用户名取决于你的系统允许什么。电子邮件相对简单:

在您的函数文件中进行验证(或任何地方):

代码语言:javascript
复制
function validateEmail($emailToValidate)
{
    return filter_var($emailToValidate, FILTER_VALIDATE_EMAIL);
}

在你的注册文件中:

代码语言:javascript
复制
if (validateEmail($_POST['email']) === false) {
    // Return/call the view and pass in the error
}
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/194271

复制
相关文章

相似问题

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