可以使用PHP“;如果;关于SELECT查询结果的语句,以确定执行INSERT查询是否会导致意外的数据重叠?

可以使用PHP“;如果;关于SELECT查询结果的语句,以确定执行INSERT查询是否会导致意外的数据重叠?,php,mysql,sql,database,Php,Mysql,Sql,Database,我是一名学生,这是我第一次写一个软件。它是LAMP堆栈上的web应用程序,作为该web应用程序的一部分,我编写了以下函数,用于在创建新用户时与数据库交互: public function CreateUser($username, $password, $email){ global $DBHandler, $SQLStatement; $SQLStatement = $DBHandler->prepare("SELECT id FROM users WHERE username

我是一名学生,这是我第一次写一个软件。它是LAMP堆栈上的web应用程序,作为该web应用程序的一部分,我编写了以下函数,用于在创建新用户时与数据库交互:

public function CreateUser($username, $password, $email){
  global $DBHandler, $SQLStatement;
  $SQLStatement = $DBHandler->prepare("SELECT id FROM users WHERE username = :username AND verified > 0");
  $SQLStatement->bindParam(':username', $username);
  $SQLStatement->execute();
  $check = $SQLStatement->fetch();
  if ($check['id']){
    return 2;
  }else{
    $SQLStatement = $DBHandler->prepare("SELECT id FROM users WHERE email = :email AND verified > 0");
    $SQLStatement->bindParam(':email', $email);
    $SQLStatement->execute();
    $check = $SQLStatement->fetch();
    if ($check['id']){
      return 3;
    }else{
      /* Edited out code that generates a random verification code, a random salt, and hashes the password. */
      $SQLStatement = $DBHandler->prepare("INSERT INTO users (username, email, passwordhash, salt, verifycode) VALUES (:username, :email, :passwordhash, :salt, :verifycode)");
      $SQLStatement->bindParam(':username', $username);
      $SQLStatement->bindParam(':email', $email);
      $SQLStatement->bindParam(':passwordhash', $passwordhash);
      $SQLStatement->bindParam(':salt', $salt);
      $SQLStatement->bindParam(':verifycode', $verifycode);
      $SQLStatement->execute();
      return 1;
    }
  }
}
$DBHandler作为PHP数据对象在别处启动

它遵循以下基本步骤:

  • 查询数据库,查看是否有人已使用所需用户名验证帐户
  • 如果用户名可用,请执行另一个查询并对电子邮件执行相同的检查
  • 如果用户名和电子邮件都可用,则生成验证码、salt、散列密码,执行第三次查询将所有内容写入数据库,并返回1(表示成功)
  • 其思想是,用户名和电子邮件在您的帐户被验证(验证='1')之前不会被保留。在其他地方,如果您单击电子邮件验证链接,则会有一个脚本将您的验证列更改为“1”

    我的第一个问题是:

    人员A提议使用用户名“Lorem”,人员B有一个未经验证的帐户,该帐户也提议使用用户名“Lorem”。是否可以按以下顺序执行查询

  • 人员A的脚本确定用户名“Lorem”未被验证用户使用
  • 人员B的脚本使用用户名“Lorem”验证其帐户
  • 人员A的脚本使用用户名“Lorem”创建其未经验证的帐户
  • 我的第二个问题是:


    是否可以将此脚本中的3个查询合并为1个查询,并使用SQL而不是PHP表示的相同if/else逻辑,如果可以,是否可以提高性能和/或防止出现上述情况?

    对于更并行的解决方案,可以将
    插入设置为有条件的:

    insert  into users 
            (username, email, ...)
    select  :username, :email, ...
    where   not exists
            (
            select  *
            from    users
            where   verified > 0
                    and (username = :username
                         or email = :email)
            )
    
    这在MySQL中仍然不是100%安全的,但在实践中应该足够了


    请注意,用户创建中的并发问题是一个奢侈的问题。我只是建立了一个基本的版本,就像你拥有的一样,并专注于你网站更有趣的方面

    这里要做的是编写一个非常原始的对象关系管理器(ORM)。是否使用帮助或这是一个学术练习?您也不应该使用全局SQL语句对象。总是创建一个新的函数作为函数的一部分。我认为将盐存储在数据库中会泄露它?我会说这个项目是学术性的,但不是练习。成绩的一部分是创建一个我们个人想要创建的网站,满足我们课外生活的一些需要。其他大多数学生都在写博客、相册之类的东西,但我正在写一个类似于论坛的交流平台。我做了大量关于盐分和散列的研究,因为安全让我着迷,我得到的总体印象是盐分不是秘密的,它们对于每个用户来说都是独一无二的。所以有人可以得到所有的salt和hash,但是因为每个salt都是唯一的,所以他们不能使用彩虹表,他们仍然必须单独对每个帐户进行暴力攻击。所以说得清楚一点,我描述的场景可能真的会发生吗?我知道这不太可能,但这种可能性是否意味着我的代码肯定是错误的做法?如果我继续这样做,像这样的并发可以做各种各样的讨厌的事情,不是吗?是的,这可能会发生,而且在很多网站上都会发生。这其实并不重要,因为很少有网站有大量的注册,用户总是可以使用不同的名称重试。而不是编写用户管理代码,试图解释许多罕见的情况下,你可以考虑使用OpenID,像StAcExopp.0。