2

考虑到一个项目将由多个开发人员工作并且将不断更新和维护,考虑到可读性和安全性,以下两个代码中的哪一个可以被认为是 PHP 的最佳实践?如果我们从性能上讲,第二个选项可能会好一点,但是有一些方法可以解决这一点。

选项1

$user = $_POST['user'];
$pass = $_POST['pass'];

// Prevent SQL Injection and XSS
$user = anti_injection($user);
$pass = anti_injection($pass);

if(strlen($user) <= 8 && strlen($pass) <= 12)
{
    // SQL query
    $sql = "SELECT id 
            FROM users 
            WHERE username = '$user' AND password = '$pass';";
}

选项 2

// Retrieve POST variables and prevent SQL Injection and XSS
$_POST['user'] = anti_injection($_POST['user']);
$_POST['pass'] = anti_injection($_POST['pass']);

if(strlen($_POST['user']) <= 8 && strlen($_POST['pass']) <= 12)
{
    // SQL query
    $sql = "SELECT id 
            FROM users 
            WHERE username = '" . $_POST['user']. "' AND password = '" . $_POST['pass'] . "';";
}

编辑 1

我没有使用 MySQL,我的数据库是 PostgreSQL

4

5 回答 5

7

也不要这样做。

我只能假设anti_injection是某种自定义过滤功能,这是一个 Bad Idea™。如果你真的想采用这些想法中的任何一个,你应该使用mysql_real_escape_string.

编写 SQL 查询时保持安全的唯一方法是使用参数,例如通过 MySQLi。无论如何,这些mysql_*功能已被弃用,因此您最好尽快迁移。

实际上mysql_real_escape_string并不是对注入攻击的万无一失的防御。考虑查询中的整数比较,例如WHERE $var > 30. 我可以成功注入1=1 or 100$var并完全打破逻辑。

参数将数据与查询语言完全分离,完全降低了注入风险。服务器接收包含参数表示法的查询和要插入的一组值,因此它可以完全不同地处理查询语言和数据。

此外,您似乎以明文形式存储密码。这是 个坏 主意。您应该研究一种强大的密码存储哈希算法,例如 bcrypt,这使得从哈希中获取明文密码变得非常困难。

MD5 和 SHA1 不是密码存储的理想选择,因为它们的设计速度很快,这意味着攻击者甚至可以快速破解强加盐密码。现代 GPU 每秒可以实现 50 亿次 MD5 哈希。事实上,有人拥有专门的哈希破解设备,其中一些可以以每秒 450 亿哈希的速度破解 MD5 。

您还应该看看这些很棒的问题,它们完全涵盖了 SQL 注入攻击、密码存储和许多其他安全问题:

更新:您提到您正在使用 postgres。您可以使用PDO从 PHP 运行参数化查询,如此处简要描述的

于 2012-07-13T14:51:53.993 回答
3

这些选项都不是最佳实践anti_injection,即使只是对于几乎可以肯定不会像宣传的那样工作的可疑人员。还有以下问题:

  • 在验证之前修改您的输入数据
  • 存储明文密码
  • 手动构造 SQL 查询,而不是使用绑定参数

根据项目的范围,最后一个可能是可以接受的。

关于性能,第一个选项理论上会更快,因为它执行的数组索引更少。但是差异肯定会很小,以至于根本无法观察到。第一个选项也更具可读性并提供更好的抽象,所有这些都只需要可忽略不计的额外内存量。

于 2012-07-13T14:49:49.923 回答
2

两者都是错误的。您似乎将密码存储为纯文本,这只是自找麻烦。

另外,如果我的用户名是"123456",我将无法登录,因为您会将其转义到\"123456\"“长度 <= 8”检查失败。

于 2012-07-13T14:49:54.480 回答
1

如果您想要安全性和性能,我建议您使用 PDO。使用参数时不能进行 sql 注入。下面是一个示例,您需要“定义”mysql 参数才能使下面的工作。

这也允许数据库缓存您的查询,因为每次使用不同的参数执行时它都不会改变,这也会提高性能。

:p_user and :p_pass

用于表示参数和

array ( ':p_user' => $user, ':p_pass' => $pass ' )

将参数设置为您需要传入的值。

您还应该考虑添加密码盐,并将该密码的 sha1 存储在数据库中,这样如果您的数据库遭到破坏,您的密码就不会清楚地暴露给黑客。

class users{ 
  function __construct() {
    define('MySQLHost', 'localhost');
    define('MySQLName', 'databasename');
    define('MySQLUser', 'username');
    define('MySQLPass', 'password');
    define('pwsalt', 'tScgpBOoRL7A48TzpBGUgpKINc69B4Ylpvc5Xc6k'); //random characters
  }

  static function GetQuery($query, $params)
  {
    $db = new PDO('pgsql:host='.MySQLHost.';dbname='.MySQLName.';', MySQLUser, MySQLPass);
    $cmd = $db->prepare($query);
    $cmd->execute($params);

    if($cmd->rowCount()==0)
      return null;
    return $cmd->fetchAll();
  }

  static function GetUser($user, $pass)
  {
    $query = "select id
      from `users`
      where username = :p_user and password = :p_pass";

    $rows = users::GetQuery($query, array(
      ':p_user' => $user,
      ':p_pass' => sha1($pass.pwsalt) //Append the salt to the password, hash it
    ));

    return $rows;
  }
}

$user = $_POST['user'];
$pass = $_POST['pass'];
if( strlen($user) <= 8 && strlen($pass) <= 12 )
{
  $result = users::GetUser($user, $pass);
  if($result != null)
    print 'Login Found';
}
于 2012-07-13T14:51:09.533 回答
0

虽然我同意其他海报,但不要试图“重新发明轮子”关于 SQL 安全性,问题似乎是关于性能以及如何使用超全局变量。

作为最佳实践,不要改变超全局变量($_GET、$_POST、$_REQUEST、$_SYSTEM 等)。我想不出一个违反此规则会提高性能的示例,并且任何修改都会导致不确定性和路上的混乱。

所以在这种情况下,这两个选项都不正确。Option1 不必要地复制一个变量(根据 Google 性能文档,这是一个禁忌)。选项 2 改变了超全局变量,这违反了上面的准则。相反,请执行以下操作:

$user = anti_injection( $_POST['user'] );
$pass = anti_injection( $_POST['pass'] );

if( strlen($user) <= 8 && strlen($pass) <= 12 ) ...

但是我要重申,“自制卫生”是一个可怕的前景,其他评论员已经对这一点进行了相当详尽的阐述。

于 2012-07-13T16:11:14.603 回答