具有动态准备语句的安全、可扩展数据库

Secure, extensible database with dynamic prepared statements

问题: 我的任务是创建一个数据库来保存有关各种产品的信息,并创建 RESTful api 来提供和管理这些信息。但客户并不确切知道他们需要这些产品的所有信息,因此数据库可能会在以后添加新列和 tables 以容纳新的产品属性。我的问题是关于生成一个可以轻松接受这些更改的数据库,并构建可以根据尚不存在的产品属性安全地获取产品的查询,几乎不需要修改。

建议的解决方案: 我有一个具有以下结构的测试数据库设置。

+------------------+
|      item        |
+----+------+------+
| id | name | cost |
+----+------+------+
|  0 | test |   50 |
+----+------+------+

+--------------+
|    color     |
+----+---------+
| id |   val   |
+----+---------+
|  0 |  blue   |
|  1 | purple  |
+----+---------+

+--------------------+
|      item_color    |
+---------+----------+
| item_id | color_id |
+---------+----------+
|       0 |        0 |
|       0 |        1 |
+---------+----------+

'item' table 稍后可能会添加列,并且可能还会添加更多的连接点 table。

检索产品的请求 http://www.example.com/api/products?color=purple&cost=50 使用 php 我正在动态构建准备好的语句来检索相关产品,希望不会为 sql 注入打开大门。首先,我使用以下函数确定哪些属性包含在 'item' table 中,哪些属性在单独的 table 中:

function column_exists($column, $pdo) {
    $statement = $pdo -> prepare("DESCRIBE item");
    $statement -> execute();
    $columns = $statement -> fetchAll(PDO::FETCH_COLUMN);
    $column_exists = in_array($column, $columns);
    return $column_exists;
}

function table_exists($table, $pdo) {
    $statement = $pdo -> prepare("SHOW TABLES");
    $statement -> execute();
    $tables = $statement -> fetchAll();
    $table_exists = in_array($table, $tables);
    return $table_exists;
}

如果 属性 在 'item' table 中找不到列或 table 名称,则会抛出异常。

我的代码构造的准备好的语句如下所示:

$sql = "SELECT * FROM item WHERE cost = :cost
AND id IN (SELECT item_id FROM item_color 
WHERE color_id IN (SELECT id FROM color WHERE val = :color));";

并且会像这样执行,

$statement = $pdo -> prepare($sql);
$statement -> execute(Array(":cost" => $cost, ":color" => $color));

我想知道的: 随着数据库的增长和更频繁的访问,我是否会遇到主要瓶颈?我的检索方法对一阶 sql 注入攻击安全吗?

我做了什么: 我已经阅读了基本的数据库设计原则和基本的 sql 注入 attack/defense 方法。我试图阅读有关动态创建准备好的语句的信息,但我发现的 material 不是我要找的。我已经针对基本 Bobby Tables attack.

测试了我的设计

为什么这个问题是相关的: 我读过的设计原则警告不要让数据库过于灵活 那些刚接触该领域的人没有办法判断灵活程度有多灵活,并且可以从对这个例子的分析中获益。此外,准备好的语句似乎只打算用作静态模板。如果我发现这种构建它们的动态方式很诱人,那么其他新手可能也会如此,所以我们需要知道我们是否正在创建一个很大的安全漏洞。最后,我一起问了这些问题,因为数据库设计和查询的结构 运行 直接相关。

详情: php v5.6.17 mysql v5.6.35

我对你的 SQL 的第一反应是你真的需要学习如何在 SQL 中使用 JOIN。连接操作是 SQL 和关系数据的基础。仅使用子查询代替 JOIN 就像使用另一种编程语言,但拒绝使用 while() 循环。当然可以,但是 为什么?

$sql = "SELECT * FROM item WHERE cost = :cost
AND id IN (SELECT item_id FROM item_color 
WHERE color_id IN (SELECT id FROM color WHERE val = :color));";

应该是

$sql = "
 SELECT i.id, i.name, i.cost, c.color 
 FROM item AS i
 INNER JOIN item_color AS ic ON i.id = ic.item_id
 INNER JOIN color AS c ON c.id = ic.color_id
 WHERE i.cost = :cost AND c.val = :color";

关于 SQL 的任何参考或教程都涵盖了联接。

至于你关于安全的问题,是的——使用查询参数对于 SQL 注入是安全的。通过对基本查询进行硬编码并将动态部分分离为参数,可以消除不安全数据更改 SQL 查询解析的任何机会。

您可能会喜欢我的介绍SQL Injection Myths and Fallacies (video: https://www.youtube.com/watch?v=VldxqTejybk)。

你的要求让我认为你最好使用像 MongoDB 这样的文档数据库,你可以在其中向任何文档添加属性。这并不意味着您仍然不必对数据库设计保持谨慎,而是让您有机会更轻松地在设计后添加属性。