这个PHP身份验证功能如何破解?

时间:2010-10-05 23:27:43

标签: php security authorization

这是我写的一个递归函数,用于确定给定用户是否有权查看页面上的内容。它基本上以下列方式被称为:

if(authorize($_SESSION['user']['user_id'], $necessaryClearance)){
    //Output restricted content
} else{
    //Inform user they are not authorized
}

每个用户都有清除级别和清除状态。这允许使用$ clearance作为用户必须匹配或击败的许可级别,用户必须匹配的许可状态或状态数组(用户可以匹配的任何一个)来调用授权功能。通常,$ user_id是从会话数据中提取的($ _SESSION ['user'] ['$ user_id'],每次页面加载时从数据库刷新),并且每页或每个显式设置清除 - 模块基础。

//This function checks if the user is authorized to view the page
//It returns 1 if access is granted and a 0 if access is denied
function authorize($id, $clearance){
 //$clearance == array
    if (is_array($clearance)){
   //if yes Iterate array through Authorize($id, $clearance[])
  foreach($clearance as $userStatus){
   $tally += authorize ($id, $userStatus);

  }
   return $tally;
 //if no check if $clearenance is equal to a string
 }else if (is_string ($clearance)){
  $string = "SELECT status

  FROM users
      WHERE id = '$id'
      LIMIT 1";
  //If result returned.
  if($userData = mysql_fetch_array(Query($string))){
   if($clearance == $userData['status']){
    return 1;
   }else{ 
    return 0;
   }
  } else{
   return 0;
  }
  // if no check if $clearance is equal to a number  
 }else if(is_numeric($clearance)){
  $string = "SELECT level
      FROM users
      WHERE id = '$id'
      LIMIT 1";
  //If result returned
  if($userData = mysql_fetch_array(Query($string))){ 
   // if number is less than or equal to clearance level allow access
   if($userData['level'] <= $clearance){
    return 1;
   }else{ 
    return 0;
   }
  } else{
   return 0;
  }

 }else{
  //if nothing matches the page dies
  die('Authorization has failed.');
 }
}

代码中是否存在明显的安全漏洞?

5 个答案:

答案 0 :(得分:6)

是。你没有在$id参数上进行任何转义!

这意味着您的查询容易受到 SQL Injection 攻击。

答案 1 :(得分:2)

SQL注入是一个严重的风险,你应该尽一切可能来防御它。即使您认为$ user_id来自会话数据,您仍然需要考虑会话数据的来源。你说它是数据库,但它是如何进入数据库的?

只是防守代码。在这种情况下,这非常简单易行 - 只需将$ user_id强制转换为整数,您可以确保在将其插入查询时不会出现额外的SQL语法。

此外,不必为您的函数使用递归。以下是以更简单的方式执行相同功能的示例:

function authorize($user_id, $clearance) {
  // coerce to integer to defend against SQL Injection
  $user_id = (int) $user_id;

  $sql = "SELECT status FROM users WHERE id = {$user_id}";
  $userData = mysql_fetch_array(Query($sql));

  $tally = 0;
  foreach ((array) $clearance as $userStatus) {
    if (is_numeric($userStatus)) {
      $tally += ($userData["level"] <= $userStatus);
    } else {
      $tally += ($userData["status"] == $userStatus);
    }
  }

  return $tally;
}

这个更简单的代码唯一不支持的是$ clearance中的嵌套数组。但你真的需要支持吗?

PS:我还建议您切换到PDO。它易于使用并支持带参数的SQL查询,这对SQL注入更有效。例如:

    $sql = "SELECT status FROM users WHERE id = ?";
    $stmt = $pdo->prepare($sql);
    $result = $stmt->execute(array($user_id));
    $userData = $stmt->fetch();

答案 2 :(得分:0)

您没有提供足够的信息。 $id来自哪里?它是get / post / cookie值吗?因为如果是,那么你可以说$id=1。这称为“不安全的直接对象引用”。

还有SQL Injection的情况。您可以注入一个简单的重言式,例如' or 1=1或做一些更隐蔽的事情,例如' and 0=1 union select "<?php eval($_GET[e])?>" into outfile /var/www/backdoor.php

答案 3 :(得分:0)

只要将user_id保存在一个变量中,该变量不会被用户改变(即$ _SESSION)并且$ clearance也不可变,那么你应该是安全的。

答案 4 :(得分:0)

即使你知道现在100%来自$ id,但是假设情况总是如此,这是不安全的。如果你的应用程序增长了如果你有更多的人在做什么怎么办?如果他们在$ _SESSION值旁边使用不同的值调用此函数会怎样?当然你可能知道你的应用程序的来龙去脉,你知道这可能永远不会发生,但它仍然是不好的做法。至少,您可以使用mysql_real_escape_string。

编辑最好在最后一点确保安全。否则你会打开门。如果我们的工作是确保绝对没有带炸弹的乘客进入飞机,哪里最安全,最安全的地方可以验证?在外面的停车场,机场的前门,还是乘客登机前?