这个PHP代码对于登录系统是否安全?

时间:2010-12-15 05:40:37

标签: php security login

这只是其中的一部分,但它首先进行连接,然后检查用户名是否存在,然后将数据插入表中。 我对PHP并不是很了解,所以没有必要扯到我身上。只是想在这里学习,我想知道我是否走在正确的轨道上。

require("constants.php");
try {  
  $DBH = new PDO("mysql:host=$host;dbname=$dbname", $dbconnect, $dbpass);  
  $DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );    
}  
catch(PDOException $e) {  
    echo "sorry, something happened. try going back and try again.";  
    file_put_contents('PDOErrors.txt', $e->getMessage(), FILE_APPEND);  
}  

function checkName(){
$STH = $DBH->query('SELECT username FROM users WHERE username = $username');
$STH->setFetchMode(PDO::FETCH_OBJ);
while($row = $STH->fetch()) {
    if($username != $row->username){
    $check = 1;
    }
    else{
    $check = 0;
    }
    return $check;
}
function createSalt()
{
    $string = md5(uniqid(rand(), true));
    return substr($string, 0, 3);
}
function register(){
$check = checkName();
if($check == 1){
$salt = createSalt();
$hash = sha1($salt . $hash);
$data = array($username, $hash, $salt, $ip);
$STH = $DBH->("INSERT INTO users (username, password, salt, ip) values (?, ?, ?)");
$STH->execute($data);
}
}

3 个答案:

答案 0 :(得分:7)

  1. 您没有定义$ username,而是在单引号字符串中使用它,因此SELECT查询无法执行

  2. 您应该使用预准备语句并将用户名绑定到SELECT查询中的参数,而不是将用户名直接粘贴到字符串中

  3. 您无需选择具有该用户名的用户以查看其是否已被使用,您只需选择计数并查看其是否为非

  4. 如果SELECT查询没有返回任何行,则checkName函数不返回任何值,因为您只在循环中返回行

  5. 我希望这些评论很有用。

答案 1 :(得分:4)

  1. 使用prepared statements,而不是内联变量
  2. 停止使用全局变量。请改用$_POST['name']
  3. if($username != $row->username){什么??!?!由于用户名是唯一的 - 它可以是1个正确的行(始终正确)或0行。

答案 2 :(得分:2)

不,它不安全/写得不好。它假设已启用寄存器全局变量,并且使用全局变量代替函数参数会使代码很难移植到不同的上下文。抛开糟糕的格式,完全没有任何注释会使代码的维护变得困难 - 良好的编码风格是良好编程的先决条件 - 因而也是安全性。

代码本身也存在特定问题。它假设$ username被引用,将无法有效运行。并且由于您将$ username字符串与数据库返回的内容进行比较,因此显然没有正确转义,这意味着代码对注入攻击是开放的。既然你正在使用PDO,那么解决方案就是简单地使用一个准备好的语句/变量绑定 - 你实际上已经使用INSERT了!

迭代每个匹配的用户名没有任何意义,严重破坏了这种行为。一个更好的方法(但不是正确的方法)将是:

/**
 * @param username string - a candidate username
 * @param DBH - connected PDO object referencing user database
 * @return bool - true if username does not exist already
 *
 * search the current list of users to see if the candidate username is available
 */
function checkName($username, $DBH){      
  $STH = $DBH->prepare('SELECT username FROM users WHERE username = :username');
  $STH->execute(array(':username'=>$username));
  $STH->setFetchMode(PDO::FETCH_OBJ);
  $row = $STH->fetch();
  if ($row === false) {
     die('whoops!');
  }
  return $username!==$row->username;
}

正确的解决方案:假设您的用户名是唯一的(并且它们确实应该是这样),那么在INSERT之前不要检查用户名是否存在 - 创建唯一索引并在INSERT之后检查重复键失败。

接下来,在初始连接周围使用try / catch,但不对后续查询进行错误检查。捕获异常后,虽然您记录并报告错误,但此时似乎没有解决控制流以防止其余代码执行。

抱歉 - 这不是好代码,更不用说安全了。