这是我写的一个递归函数,用于确定给定用户是否有权查看页面上的内容。它基本上以下列方式被称为:
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.');
}
}
代码中是否存在明显的安全漏洞?
答案 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。
编辑最好在最后一点确保安全。否则你会打开门。如果我们的工作是确保绝对没有带炸弹的乘客进入飞机,哪里最安全,最安全的地方可以验证?在外面的停车场,机场的前门,还是乘客登机前?