具有动态预处理语句的安全,可扩展的数据库

时间:2017-10-18 21:16:24

标签: php mysql database pdo sql-injection

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

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

+------------------+
|      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'表可能会在以后添加列,并且可能也会添加更多的联结表。

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

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”表中找到该属性作为列,或者作为表名,则抛出异常。

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

$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注入攻击/防御方法的内容。我试着阅读有关动态创建预准备语句的内容,但我找到的材料并不是我想要的。我已根据基本Bobby Tables attack测试了我的设计。

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

详细信息: php v5.6.17 mysql v5.6.35

1 个答案:

答案 0 :(得分:0)

我对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的任何参考或教程都涵盖了连接。

至于你关于安全性的问题,yes-using查询参数对于SQL注入是安全的。通过对基本查询进行硬编码并将动态部分分离为参数,可以消除不安全数据更改SQL查询解析的可能性。

您可能会喜欢我的演示文稿SQL Injection Myths and Fallacies(视频:https://www.youtube.com/watch?v=VldxqTejybk)。

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