Doctrine:传递 table 名称作为参数
Doctrine: Pass table name as parameter
我在 Symfony 上使用 Doctrine,需要安全地将动态 table 名称传递给 SQL 查询,因为“用户”需要能够选择备份 table 从中恢复值。
我的问题是,当我像下面的代码一样将它作为参数传递时,table 名称被放在撇号中,这导致 SQL 语法不正确。
$sql = "UPDATE article a LEFT JOIN :restoretable rt USING (articleid)
SET a.stock = rt.stock";
$stmt = $conn->prepare($sql);
$stmt->execute(['restoretable' => $restoretable]);
旧代码使用 sprintf("UPDATE article a LEFT JOIN %s ...", $restoretable) 并且容易 SQL 注入。
因此我的问题是:如何安全地将 table 名称(或行名称,或任何其他不能用撇号转义的名称)传递给我的查询?
提前致谢
可能的解决方案。无法像您描述的那样传递 table 名称。
$metadata = $this->getEntityManager()->getClassMetadata(MyEntityClass::class);
$metadata->setPrimaryTable([
'name' => 'myDynamicTableName'
]);
$qb = $this->createQueryBuilder('c');
$result = $qb
->getQuery()
->getArrayResult();
但在此示例中,您将需要知道什么是 table 名称和实体 class 名称。
关于 table 名称和 SQL 注入。最好的 if table 名称将只包含英文字母和下划线。就是这样。
https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/security.html#user-input-and-doctrine-orm
有关不安全的 dql 查询的更多信息。
在研究了 PDO 提供的可能性以及 Doctrine 的可能性之后,我得出以下结论:
正如 michal 所述,不可能通过参数传递 table 名称,无论是在 Doctrine 中还是在 PDO 中。必须手动验证有效性。
两种可能的解决方案如下(也有其他解决方案,但我在这里描述的是我现在使用的):
第一个选项 更好,因为用户输入永远不会接近 table。但是,table 名称必须在编码时已知。我用它来复制现有的已知 table.
switch($userinput) {
case "art": $tablename="article"; break;
case "cus": $tablename="customer"; break;
default: return false;
}
$newtablename = $tablename."_".date("YmdHis");
$conn = $this->em->getConnection();
$stmt = $conn->prepare(sprintf("CREATE TABLE %1$s LIKE %2$s;
INSERT %1$s SELECT * FROM %2$s",
$newtablename,$tablename));
$stmt->execute();
选项二 的优点是您可以使用未明确知道的 table 名称。但是,table 名称不应该是完全随机的,因此至少我们应该知道命名格式。在我的例子中是 "article_".date("YmdHis")
.
我们现在可以使用正则表达式来检查用户输入的有效性(在本例中:
$restoretable = $form->get('restoretable')->getData();
if (preg_match("/^article_[0-9]{14}$/i", $restoretable)) {
$conn = $this->getDoctrine()->getManager()->getConnection();
$sql = sprintf("UPDATE article a LEFT JOIN %s rt USING (articleid)
SET a.stock = rt.stock", $restoretable);
$stmt = $conn->prepare($sql);
$stmt->execute();
}
else {
return false;
}
一定要彻底检查正则表达式。
我在 Symfony 上使用 Doctrine,需要安全地将动态 table 名称传递给 SQL 查询,因为“用户”需要能够选择备份 table 从中恢复值。
我的问题是,当我像下面的代码一样将它作为参数传递时,table 名称被放在撇号中,这导致 SQL 语法不正确。
$sql = "UPDATE article a LEFT JOIN :restoretable rt USING (articleid)
SET a.stock = rt.stock";
$stmt = $conn->prepare($sql);
$stmt->execute(['restoretable' => $restoretable]);
旧代码使用 sprintf("UPDATE article a LEFT JOIN %s ...", $restoretable) 并且容易 SQL 注入。
因此我的问题是:如何安全地将 table 名称(或行名称,或任何其他不能用撇号转义的名称)传递给我的查询?
提前致谢
可能的解决方案。无法像您描述的那样传递 table 名称。
$metadata = $this->getEntityManager()->getClassMetadata(MyEntityClass::class);
$metadata->setPrimaryTable([
'name' => 'myDynamicTableName'
]);
$qb = $this->createQueryBuilder('c');
$result = $qb
->getQuery()
->getArrayResult();
但在此示例中,您将需要知道什么是 table 名称和实体 class 名称。
关于 table 名称和 SQL 注入。最好的 if table 名称将只包含英文字母和下划线。就是这样。
https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/security.html#user-input-and-doctrine-orm 有关不安全的 dql 查询的更多信息。
在研究了 PDO 提供的可能性以及 Doctrine 的可能性之后,我得出以下结论: 正如 michal 所述,不可能通过参数传递 table 名称,无论是在 Doctrine 中还是在 PDO 中。必须手动验证有效性。
两种可能的解决方案如下(也有其他解决方案,但我在这里描述的是我现在使用的):
第一个选项 更好,因为用户输入永远不会接近 table。但是,table 名称必须在编码时已知。我用它来复制现有的已知 table.
switch($userinput) {
case "art": $tablename="article"; break;
case "cus": $tablename="customer"; break;
default: return false;
}
$newtablename = $tablename."_".date("YmdHis");
$conn = $this->em->getConnection();
$stmt = $conn->prepare(sprintf("CREATE TABLE %1$s LIKE %2$s;
INSERT %1$s SELECT * FROM %2$s",
$newtablename,$tablename));
$stmt->execute();
选项二 的优点是您可以使用未明确知道的 table 名称。但是,table 名称不应该是完全随机的,因此至少我们应该知道命名格式。在我的例子中是 "article_".date("YmdHis")
.
我们现在可以使用正则表达式来检查用户输入的有效性(在本例中:
$restoretable = $form->get('restoretable')->getData();
if (preg_match("/^article_[0-9]{14}$/i", $restoretable)) {
$conn = $this->getDoctrine()->getManager()->getConnection();
$sql = sprintf("UPDATE article a LEFT JOIN %s rt USING (articleid)
SET a.stock = rt.stock", $restoretable);
$stmt = $conn->prepare($sql);
$stmt->execute();
}
else {
return false;
}
一定要彻底检查正则表达式。