0

我为我的网站制作了一个登录脚本,需要一些关于它是否安全以及如何改进它的反馈:

<?php
  session_start();

  $user = "root";
  $host = "localhost";
  $pass = "";
  $db = "test_db";

  $cxn = mysqli_connect($host, $user, $pass, $db) or die ("Couldn't connect to the server. Please try again.");

  if(isset($_POST['submit'])) {
    $username = mysql_real_escape_string(strip_tags(trim($_POST['username'])));
    $password = mysql_real_escape_string(strip_tags(trim($_POST['password'])));
    $message = "";

    $stmt = $cxn->prepare('SELECT * FROM users WHERE username = ?');
    $stmt->bind_param('s', $username);

    $stmt->execute();

    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
      if(password_verify($password, $row['password'])) {
        $_SESSION['username'] = $username;
        $_SESSION['password'] = $password;
        header("location:index.php");
      } else {
        $message = "The username or password is incorrect.";
      }
    }
}

?>

另外,我只是在学习会话并且有几个问题:

  1. 用户成功登录后,我需要他们的用户名显示在任何其他页面上。如何使会话安全?
  2. 如何使“注销”功能结束会话?
4

3 回答 3

3

首先,您不应该在密码字段上应用 mysql_real_escape_string。在与存储在数据库中的哈希值进行比较之前,您要对字符串进行转义,因此如果密码包含特殊字符(如 ' 或 " ),它们将被转义。这将更改输出哈希值,并且登录将不起作用。

举个例子,密码 pass123' 将变成 pass123\',它会有不同的哈希值。

其次,最好不要将明文密码存储在会话中,因为这意味着它们会被写入(默认情况下未加密)到磁盘上的文件中;即使它们只是服务器端,如果服务器受到威胁,所有登录帐户也会受到威胁。如果在登录后,您只需使用登录的用户信息设置用户 ID 或数组/对象,那就更好了。如果存在,您知道用户已登录,并且要注销,您只需从会话中取消设置变量。

第三,为了使 MySQLi 完全无风险,您应该使用准备好的语句和 PDO。例如:

<?php
    $dbh = new PDO("mysql:host=$host;dbname=$db;charset=utf8'", $user, $pass);

    if(isset($_POST['submit'])) {
        // Prepare the statement
        $stmt = $dbh->prepare("SELECT * FROM users WHERE username=:username");

        // Bind the parameters
        $stmt->bindParam(':username', $_POST['username'], PDO::PARAM_STR);

        // Execute the statement
        $stmt->execute();

        // Get the result
        $user= $stmt->fetch(PDO::FETCH_OBJ);

        if (empty($user) || !password_verify($_POST['password'], $user->password)) {
            $message = 'Login Failed';

        // Login is ok, store the user in the session
        } else {
            $_SESSION['loggedInUser'] = $user;

            $message = 'You are now logged in!';
        }
    }
于 2013-08-22T17:58:35.720 回答
2
  1. 会话是服务器端的,因此您不必担心这种意义上的安全性。您只需session_start()在开始时包含,然后echo $_SESSION['username'];在您想要的任何时候包含。

  2. 要结束会话,请使用session_destroy().


就像我在评论中所说的那样,在创建登录脚本时,您需要使用参数化查询来真正“安全”。

于 2013-08-22T17:18:04.923 回答
1
$password = mysql_real_escape_string(strip_tags(trim($_POST['password'])));

为什么要从密码中删除空格和标签?如果我随机生成的密码是<correct horse battery stapl/>e怎么办?您的代码会将密码转换为e. 这就是你应该用密码做的所有事情:

$password = mysql_real_escape_string($_POST['password']);

编辑:实际上,您根本不需要转义密码,因为您没有在查询中使用它。


关于您的编辑:

$username = mysql_real_escape_string(strip_tags(trim($_POST['username'])));
$password = mysql_real_escape_string(strip_tags(trim($_POST['password'])));
$message = "";

$stmt = $cxn->prepare('SELECT * FROM users WHERE username = ?');
$stmt->bind_param('s', $username);

使用参数化查询,您不需要转义任何东西(这是最大的优势),所以这应该是:

$username = trim($_POST['username']);
$password = $_POST['password'];
$message = "";

$stmt = $cxn->prepare('SELECT * FROM users WHERE username = ?');
$stmt->bind_param('s', $username);

对于一些好消息,这可能是我看到有人正确处理密码的第一个 Stack Overflow PHP 问题:

if(password_verify($password, $row['password'])) {

是的。

于 2013-08-22T17:35:58.980 回答