提问人:suverenia 提问时间:2/15/2021 最后编辑:Dharmansuverenia 更新时间:2/16/2021 访问量:176
开关函数SQL注入安全吗?
Is the switch-function SQL-injection safe?
问:
我正在尝试访问具有变量列名的数据库中的条目。
我有这个表格,其中包含可以属于以下三个类别(汽车、自行车、卡车)之一的车辆:
车辆 | 汽车 | 自行车 | 卡车 |
---|---|---|---|
汽车 1 | x | ||
汽车 2 | x | ||
自行车 1 | x | ||
卡车 1 | x |
使用 OOP 和 PDO,我正在尝试访问属于某个类别的车辆。这样:
用户输入:
URL: ?category=cars
以下所有内容都在名为“Vehicles”的类中。
类车辆的构造者:
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 注入?
我确实意识到我使用的是糟糕的数据库设计,因为用户不应该获得有关数据库列名称的任何提示。我也知道我应该避免与数据库相关的错误消息,这些错误消息可能对黑客有用(这就是我使用默认开关的原因)——但我现在需要使用当前的数据库模型进行快速修复。
答:
简短的回答: 是的
长答案:
是的,你是, 因为你实际上并没有将用户输入用作数据库字段,所以他们无法操作它。
只要您不直接将用户输入放入数据库中,您就不会遇到 mysql 注入问题。
许多人告诉你在每个请求中都使用准备好的语句,但只有当你在查询中直接使用用户输入(如用户名、命令、电子邮件)时,你才必须使用它们。
评论
当您将 Coulmn 作为参数时,您运行的设计很糟糕。
但是,代码原样是安全的,因为它是固定的。
请至少做。 取代: $sql = “从车辆中选择 * $this->类别不为 NULL”;
$param = $usetheconnection->real_escape_string($this->类别); $sql = “从'{$param}'不为空的车辆中选择*”;
然后重做设计
看起来这个问题(“安全吗?”)已经得到了解答。然而,如何以及为什么似乎有点悬而未决,所以这里有一些进一步的信息......
为什么安全
在这种情况下,使您免于SQL注入的一件事是您在语句中设置了一个大小写。如果没有这个词,你就会对SQL注入持开放态度。让我们来玩一玩:default
switch
有效输入示例
- 用户输入
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
- 带 (PHP 8+)
match
- 使用 和 查找
array
- 通过根据数据库架构检查变量
开关
我看到你这样做的最大问题是,如果有人来查看你的代码,他们很可能会看到你已经有效地设置了默认值,因此删除了大小写;这将使您对 SQL 注入持开放态度。switch
default
因此,您应该相应地更新代码:
- 切勿将用户输入设置为属性
category
- 设置 on 声明的默认属性
category
F.D.(英语:F.D.)
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
将抛出错误;或者更确切地说,如果提供了无法匹配的值!
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 = ?
现在,您可以使用准备好的语句来确保完全安全
注意
创建两个表并将现有表更新到其中似乎是一个耗时的过程。但实际上,这不会花那么长时间。过程:
- 创建具有适当数据类型、属性等的表。
DESCRIBE
表格并提取不同的类型(、等)vehicles
car
bike
- 将类型插入到表中
type
- 例如,这一切都可以通过几行PHP自动完成
- 编写一个简短的脚本,根据列遍历车辆表的每一行和记录
insert
vechicleType
not null
- 检查您的数据(根据需要进行备份)
- 从中删除不再需要的列
vehicles
评论