原则:将表名作为参数传递

问题描述 投票:0回答:3

我在 Symfony 上使用 Doctrine,并且需要安全地将动态表名称传递给 SQL 查询,因为“用户”需要能够选择要从中恢复值的备份表。

我的问题是,当我像下面的代码一样将其作为参数传递时,表名会被放在撇号中,这会导致 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("UPDATEarticle a LEFT JOIN %s ...", $restoretable) 并且容易出现 SQL 注入。

因此,我的问题是:如何安全地将表名称(或行名称,或任何其他不能用撇号转义的内容)传递给我的查询?

提前致谢

parameters doctrine
3个回答
0
投票

可能的解决方案。无法像您所描述的那样传递表名称。

    $metadata = $this->getEntityManager()->getClassMetadata(MyEntityClass::class);
    $metadata->setPrimaryTable([
        'name' => 'myDynamicTableName'
    ]);

    $qb = $this->createQueryBuilder('c');

    $result = $qb
        ->getQuery()
        ->getArrayResult();

但是在这个例子中你需要知道表名和实体类名是什么。

关于表名和SQL注入。最好表名仅包含英文字母和下划线。就是这样。

https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/security.html#user-input-and-doctrine-orm 有关不安全 dql 查询的更多信息。


0
投票

在研究了 PDO 以及 Doctrine 提供的可能性之后,我得出以下结论: 正如 michal 所说,无论是在 Doctrine 中还是在 PDO 中,都不可能通过参数传递表名。有效性验证必须手动完成。

两种可能的解决方案如下(还有其他解决方案,但我在这里描述我现在正在使用的解决方案):

第一个选项更好,因为用户输入永远不会接近表格。但是,在编码时必须知道表名称。我用它来复制现有的已知表。

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();

选项二的优点是您可以使用未明确已知的表名称。然而,表名不应该是完全随机的,因此至少我们应该知道命名格式。就我而言,它是"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; }
请务必彻底检查您的正则表达式。


0
投票
如果您要使用 DQL/SQL 语句,并且确实需要使用标识符,那么您不妨使用:

$sometablename = $connection->quoteIdentifier('sometablename');
所以类似:

$table = $entityManager->getConnection()->quoteIdentifier('sometable'); $sql = 'SELECT * FROM $table`;
最终会是:

SELECT * FROM `sometable`;
但始终要小心进来的内容。即使不是 SQL 注入,有人总是可以执行“information_schema”或您不期望的不同表。

© www.soinside.com 2019 - 2024. All rights reserved.