繁体   English   中英

Doctrine:将表名作为参数传递

[英]Doctrine: Pass table name as parameter

我在 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("UPDATE article a LEFT JOIN %s...", $restoretable) 并且容易发生 SQL 注入。

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

提前致谢

可能的解决方案。 没有办法像你描述的那样传递表名。

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

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

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

但在此示例中,您需要知道表名称和实体 class 名称是什么。

关于表名和 SQL 注射。 如果表名最好只包含英文字母和下划线。 就是这样。

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

After researching about the possibilities PDO offers, along with those of Doctrine, I came to the following conclusing: As stated by michal, it is not possible to pass a table name thrugh a Parameter, neither in Doctrine, nor in 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();

选项 2的优点是您可以使用未明确知道的表名。 但是,表名不应该是完全随机的,至少我们应该知道命名格式。 就我而言,它是"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;
}

请务必彻底检查您的正则表达式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM