switch-function SQL-injection 安全吗?
Is the switch-function SQL-injection safe?
我正在尝试访问具有可变列名称的数据库中的条目。
我有这个 table 包含属于以下三个类别(汽车、自行车、卡车)之一的车辆:
vehicle
car
bike
truck
Car 1
x
Car 2
x
Bike 1
x
Truck 1
x
使用 OOP 和 PDO,我正在尝试访问属于某个类别的车辆。像这样:
用户输入:
URL: ?category=cars
以下所有内容都在名为“Vehicles”的 class 中。
class 车辆的制造商:
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 注入问题。
许多人告诉您在每次请求时都使用准备好的语句,但只有在您直接在查询中使用 userinput 时才需要使用它们,例如用户名订购电子邮件。
当你将列作为参数时,你是运行一个糟糕的设计。
然而,代码是安全的,因为它是固定的。
请至少做。
代替 :
$sql = "SELECT * 来自 $this->category 不为空的车辆";
$param = $usetheconnection->real_escape_string($this->category);
$sql = "SELECT * 来自 `{$param}` 不为空的车辆";
然后重做设计
看起来这个问题(“安全吗?”)已经得到解答。然而,如何以及为什么似乎有点悬而未决,所以这里有一些进一步的信息......
为什么它安全
在这种情况下,ONE 使您免于 SQL 注入的是您在 [=18] 中设置了一个 default
案例=] 语句。如果没有这个词,您将对 SQL 注入持开放态度。让我们一起玩吧:
有效输入示例
- 用户输入
bikes
- 您的代码将
category
属性 设置为 bikes
- 你的
switch
运行并找到 bikes
所以它 returns bike
作为 category
输入示例无效
- 用户输入
hairStraightener
- 您的代码将
category
属性 设置为 hairStraightener
- 您的
switch
运行但未找到 hairStraightener
,因此它 returns car
作为 category
;这是 default
案例
没有默认值的无效大小写
- 用户输入
hairStraightener
- 您的代码将
category
属性 设置为 hairStraightener
- 您的
switch
运行但未找到 hairStraightener
,因此 category
未更新并保持为 hairStraightener
现在,想象一下用户输入了如下内容:
1; DROP TABLE vehicles; --
// OR...
1; UPDATE TABLE vehicles SET price = 1; --
现在您丢失了大量数据,或者您商店中的所有商品都需要 1 英镑(便宜货!)
改善事物
您是对的:如果您需要将变量直接输入到 SQL 查询中,您需要将 acceptable 项目列入白名单并仅使用这些项目。有不同的方法...
- 就像你有一个
switch/case
- 有
match
(PHP 8+)
- 使用
array
和查找
- 根据数据库模式检查变量
切换
我看到的最大问题是,如果有人过来查看您的代码,他们很可能会发现您有效地设置了默认值在 switch
之前删除 default
案例;这会让你对 SQL 注入持开放态度。
所以你应该相应地更新你的代码:
- 永远不要将用户输入设置为
category
属性
- 在声明时设置
category
的默认值 属性
例如
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
语句:
注意:如果不提供 default
大小写,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
)并检查输入是否与其中一个列名匹配来自动生成 safe 字段。在您的情况下,尽管这可能不是最好的主意,因为您仍然可以让某人输入非预期的 真实字段 。也许这不会是灾难性的,但绝对不是故意的!
长期解决问题
正如其他人所说,这是一个有很大缺陷的数据库设计。大概看起来像:
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 = ?
现在您可以使用准备好的语句来确保完全安全
N.B.
创建两个 table 并将现有的更新到其中似乎是一个耗时的过程。但实际上不会花那么长时间。过程:
- 使用适当的数据类型、属性等创建 table
DESCRIBE
vehicles
table 并提取不同的类型(car
、bike
等)
- 将类型插入 table
type
- 这都可以通过几行PHP自动完成,例如
- 编写一个简短的脚本,根据
not null
的列,将车辆的每一行 table 和 insert
记录循环到 vechicleType
table ]
- 检查您的数据(按要求备份)
- 从
vehicles
中删除不再需要的列
我正在尝试访问具有可变列名称的数据库中的条目。
我有这个 table 包含属于以下三个类别(汽车、自行车、卡车)之一的车辆:
vehicle | car | bike | truck |
---|---|---|---|
Car 1 | x | ||
Car 2 | x | ||
Bike 1 | x | ||
Truck 1 | x |
使用 OOP 和 PDO,我正在尝试访问属于某个类别的车辆。像这样:
用户输入:
URL: ?category=cars
以下所有内容都在名为“Vehicles”的 class 中。
class 车辆的制造商:
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 注入问题。
许多人告诉您在每次请求时都使用准备好的语句,但只有在您直接在查询中使用 userinput 时才需要使用它们,例如用户名订购电子邮件。
当你将列作为参数时,你是运行一个糟糕的设计。
然而,代码是安全的,因为它是固定的。
请至少做。 代替 : $sql = "SELECT * 来自 $this->category 不为空的车辆";
$param = $usetheconnection->real_escape_string($this->category); $sql = "SELECT * 来自 `{$param}` 不为空的车辆";
然后重做设计
看起来这个问题(“安全吗?”)已经得到解答。然而,如何以及为什么似乎有点悬而未决,所以这里有一些进一步的信息......
为什么它安全
在这种情况下,ONE 使您免于 SQL 注入的是您在 [=18] 中设置了一个 default
案例=] 语句。如果没有这个词,您将对 SQL 注入持开放态度。让我们一起玩吧:
有效输入示例
- 用户输入
bikes
- 您的代码将
category
属性 设置为bikes
- 你的
switch
运行并找到bikes
所以它 returnsbike
作为category
输入示例无效
- 用户输入
hairStraightener
- 您的代码将
category
属性 设置为hairStraightener
- 您的
switch
运行但未找到hairStraightener
,因此它 returnscar
作为category
;这是default
案例
没有默认值的无效大小写
- 用户输入
hairStraightener
- 您的代码将
category
属性 设置为hairStraightener
- 您的
switch
运行但未找到hairStraightener
,因此category
未更新并保持为hairStraightener
现在,想象一下用户输入了如下内容:
1; DROP TABLE vehicles; --
// OR...
1; UPDATE TABLE vehicles SET price = 1; --
现在您丢失了大量数据,或者您商店中的所有商品都需要 1 英镑(便宜货!)
改善事物
您是对的:如果您需要将变量直接输入到 SQL 查询中,您需要将 acceptable 项目列入白名单并仅使用这些项目。有不同的方法...
- 就像你有一个
switch/case
- 有
match
(PHP 8+) - 使用
array
和查找 - 根据数据库模式检查变量
切换
我看到的最大问题是,如果有人过来查看您的代码,他们很可能会发现您有效地设置了默认值在 switch
之前删除 default
案例;这会让你对 SQL 注入持开放态度。
所以你应该相应地更新你的代码:
- 永远不要将用户输入设置为
category
属性 - 在声明时设置
category
的默认值 属性
例如
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
语句:
注意:如果不提供 default
大小写,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
)并检查输入是否与其中一个列名匹配来自动生成 safe 字段。在您的情况下,尽管这可能不是最好的主意,因为您仍然可以让某人输入非预期的 真实字段 。也许这不会是灾难性的,但绝对不是故意的!
长期解决问题
正如其他人所说,这是一个有很大缺陷的数据库设计。大概看起来像:
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 = ?
现在您可以使用准备好的语句来确保完全安全
N.B.
创建两个 table 并将现有的更新到其中似乎是一个耗时的过程。但实际上不会花那么长时间。过程:
- 使用适当的数据类型、属性等创建 table
DESCRIBE
vehicles
table 并提取不同的类型(car
、bike
等)- 将类型插入 table
type
- 这都可以通过几行PHP自动完成,例如
- 编写一个简短的脚本,根据
not null
的列,将车辆的每一行 table 和insert
记录循环到vechicleType
table ] - 检查您的数据(按要求备份)
- 从
vehicles
中删除不再需要的列