具有动态准备语句的安全、可扩展数据库
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 这样的文档数据库,你可以在其中向任何文档添加属性。这并不意味着您仍然不必对数据库设计保持谨慎,而是让您有机会更轻松地在设计后添加属性。
问题: 我的任务是创建一个数据库来保存有关各种产品的信息,并创建 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 这样的文档数据库,你可以在其中向任何文档添加属性。这并不意味着您仍然不必对数据库设计保持谨慎,而是让您有机会更轻松地在设计后添加属性。