我正在尝试使用可变的列名访问数据库中的条目。
我有此表,其中包含可以属于三类(汽车,自行车,卡车)之一的车辆:
车辆 | 车 | 自行车 | 卡车 |
---|---|---|---|
汽车1 | X | ||
2号车 | X | ||
自行车1 | X | ||
卡车1 | X |
使用OOP和PDO,我正在尝试访问属于某个类别的车辆。像这样:
用户输入:
URL: ?category=cars
以下所有内容都在一个名为“车辆”的类中。
车辆制造商:
public function __construct() {
$this->category = $_GET["category"] ?? "cars";
switch ($this->category) {
default: //Avoiding db-error messages by setting default category to "car"
case "cars":
$this->category = "car";
break;
case "bikes":
$this->category = "bike";
break;
case "trucks":
$this->category = "truck";
break;
}
然后,我从数据库访问与类别相对应的条目:
public function getVehiclesFromCategory() {
$sql = "SELECT * FROM vehicles WHERE $this->category IS NOT NULL";
$stmt = $this->connect()->query($sql);
while ($row = $stmt->fetch()) {
$row["vehicle"]."<br>";
}
}
然后,我创建对象以从所选类别中获取输出:
$Vehicles = new Vehicle();
$Vehicles->getVehiclesFromCategory();
我基本上是将用户输入与预定义值相关联。这足以避免SQL注入吗?
我确实意识到我使用的是错误的数据库设计,因为用户不应获得有关数据库列名称的任何提示。我还知道,我应该避免与数据库有关的错误消息,这些消息可能对黑客有用(这就是为什么我使用默认开关的原因)-但目前我需要使用当前的数据库模型进行快速修复。
好像这个问题(“安全吗?”)已经回答了。但是,如何以及为什么似乎有点悬而未决,所以这里有一些进一步的信息...
在这种情况下,使您免于SQL注入的一件事就是您default
在switch
语句中设置了大小写。没有这个词,您将很容易接受SQL注入。让我们玩一下:
有效的输入示例
bikes
category
属性设置为bikes
switch
运行并被bikes
发现,因此返回bike
为category
输入示例无效
hairStraightener
category
属性设置为hairStraightener
switch
跑步hairStraightener
未找到,因此返回car
为category
;就是这种default
情况无效情况,无默认
hairStraightener
category
属性设置为hairStraightener
switch
跑步,hairStraightener
但未找到,因此category
不会更新,并保持为hairStraightener
现在,假设用户输入了以下内容:
1; DROP TABLE vehicles; --
// OR...
1; UPDATE TABLE vehicles SET price = 1; --
现在您丢失了数据负载,或者商店中的所有商品都花费了1英镑(讨价还价!)
您处于正确的位置:如果您需要直接在SQL查询中输入变量,则需要将可接受的项目列入白名单并仅使用这些项目。有不同的方法来做...
switch/case
match
(PHP 8+)array
和查找转变
我所遇到的最大问题是,如果有人来查看您的代码,他们很可能会发现您已经在之前有效地设置了默认值switch
,从而消除了这种default
情况。这将使您易于进行SQL注入。
因此,您应该相应地更新代码:
category
属性category
on声明的默认属性例如
public $category = "car";
public function __construct()
{
switch ($_GET["category"] ?? null) {
case "cars":
$this->category = "car";
break;
case "bikes":
$this->category = "bike";
break;
case "trucks":
$this->category = "truck";
break;
}
}
比赛
正如@Dharman所评论的,如果您的服务器运行的是PHP 8+,则可以使用match
。在这种情况下,您可以将其视为类型敏感的switch
语句:
注意:match
如果您不提供包装default
盒,将抛出错误;或者更确切地说,如果提供了一个无法匹配的值!
function __construct()
{
$this->category = match($_GET["category"] ?? "cars") {
"cars" => "car",
"bikes" => "bike",
"trucks" => "truck",
default => "car"
};
}
数组查找
private $allowedFields = [
"cars" => "car",
"bikes" => "bike",
];
public function __construct()
{
$this->category = $this->allowedFields[$_GET["category"] ?? "cars"];
}
数据库架构
最后,您可以通过检查数据库模式(类似于)并检查输入是否与列名之一匹配来自动生成安全字段DESCRIBE vehicles
。在您的情况下,虽然这可能不是最好的主意,因为您仍然可以让某人输入不想要的真实字段。也许这不会是灾难性的,但这绝对不是故意的!
正如其他人所说,这是一个有缺陷的数据库设计。大概看起来像:
vehicles
id
make
model
price
...
bike
car
van
truck
...
什么时候看起来应该更像:
vehicle < vehicleType > type
id id id
make vehicle_id name
model type_id
price
...
然后,您将SQL更新为如下所示:
SELECT
vehicle.id, vehicle.make, vehicle.model, vehicle.price,
type.name
FROM vehicle
JOIN vehicleType on vehicle.id = vehicleType.vehicle_id
JOIN type on vehcileType.type_id = type.id
WHERE type.name = ?
现在,您可以使用准备好的语句来完全安全了
NB
创建两个表并将现有的表更新到其中似乎是一个耗时的过程。但实际上并不会花那么长时间。过程:
DESCRIBE
该vehicles
表和提取物中的不同类型的(car
,bike
等)type
insert
记录记录到vechicleType
表中not null
vehicles
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句