Doctrine expr - “文字”函数是否在内部使用准备好的语句?

zoz*_*ozo 1 php sql-injection prepared-statement symfony doctrine-orm

通常情况下,我们有与 setParameters 配对的 andWhere/orWhere 函数可以正确防止注入。

我有一个更复杂的案例,我想确保一切都是安全的。如果我正确阅读了教义代码,似乎使用文字具有相同的效果,但我不确定...你能吗?确认/确认以下两种情况是安全的(都使用准备好的语句和通配符注入防止 sql 注入)?

第一种情况

$expr = $queryBuilder->expr()->orX();
$expr->add($queryBuilder->expr()->lt('entityName.field - ' . $queryBuilder->expr()->literal($rule->getValue()), $queryBuilder->expr()->literal(self::MAX_ERROR))); // Since the second part is a constant (and a numeric one) it shouldn't need literal but... can't hurt.
Run Code Online (Sandbox Code Playgroud)

第二种情况

$expr = $queryBuilder->expr()->orX();
$expr->add($queryBuilder->expr()->like('entityName.field', $queryBuilder->expr()->literal(addcslashes($rule->getValue(), '%_') . '%')));
Run Code Online (Sandbox Code Playgroud)

fyr*_*rye 5

简而言之,您提供的声明不安全。

其中的方法Query\Expr不会自动将您的值转换为参数占位符。


的解释 Query\Expr::literal

有效地使用Query\Expr::literal仅将值转换为 DQL 语句的文字字符串值,将提供的值适当地用引号括起来。虽然该方法确实从提供的值中转义单引号,但这样做并不能防止所有 SQL 注入方法。[原文]

在你的第一种情况下

$expr = $em->getExpressionBuilder();
$orX = $expr->orX();

$orX->add(
    $expr->lt(
        'entityName.field - ' . $expr->literal($rule->getValue()), 
        $expr->literal(self::MAX_ERROR)
    )
);
//...

$qb = $em->createuQueryBuilder()
    ->where($orX);
dump($qb->getQuery()->getSQL());
Run Code Online (Sandbox Code Playgroud)

如果$rule->getValue()是实数,则生成的SQL语句将成为“文字”数值。

WHERE (
   (alias.column - 10 < 2)
   OR
   (...)
)
Run Code Online (Sandbox Code Playgroud)

如果$rule->getValue()self::MAX_VALUE是数字字符串(可能会产生意外结果),则生成的SQL输出将是:

WHERE (
   (alias.column - '10' < '2')
   OR
   (...)
)
Run Code Online (Sandbox Code Playgroud)

并且$qb->getQuery()->getParameters()将是一个空的ArrayCollection,因为没有添加其他参数。


防止 SQL 注入

为了确保您的语句从SQL注入的保护,你必须你的声明中声明的参数占位符和使用setParameter的参数值绑定到占位符

$orX->add(
    $expr->lt(
        'entityName.field - :rule_value', 
        ':max'
    )
);
$qb->setParameter('rule_value', $rule->getValue());
$qb->setParameter('max', self::MAX_VALUE);
Run Code Online (Sandbox Code Playgroud)

如果您有多个占位符,则需要适当地跟踪和绑定它们。

$v = 0;
for (/*...*/) {
    $param = \sprintf('rule_value%d', $v++);
    $orX->add(
        $expr->lt(
            "entityName.field - :$param", 
            ':max'
        )
    );
    $qb->setParameter($param, $rule->getValue());
}
$qb->setParameter('max', self::MAX_VALUE);
Run Code Online (Sandbox Code Playgroud)

处理嵌套标准

从您评论中的问题来看:

你如何处理 andWhere 与 orWhere 混合而没有 expr 的可变数量的条件?像 WHERE condition1 和 (orCondition1 OR orCondition2 or... OR orConditionN)

您可以创建多个嵌套条件以WHERE通过使用andX或的所需分组来提供给子句orX

$v = 0;
for (/*...*/) {
    $param = \sprintf('rule_value%d', $v++);
    $orX->add(
        $expr->lt(
            "entityName.field - :$param", 
            ':max'
        )
    );
    $qb->setParameter($param, $rule->getValue());
}
$qb->setParameter('max', self::MAX_VALUE);
Run Code Online (Sandbox Code Playgroud)

或者

$andXA = $expr>andX();
$andXB = $expr->andX();
$orX = $expr->orX();

$andXA->add('expr1');
$andXA->add('expr2');

$andXB->add('expr3');
$andXB->add('expr4');

$orX->add($andXA);
$orX->add($andXB);
Run Code Online (Sandbox Code Playgroud)

或者,您可以WHERE使用andWhere或将表达式添加到主子句部分,orWhere但您需要使用表达式构建器来更改嵌套条件分组。

$expr->orX(
   $expr->andX(
      'expr1',
      'expr2'
   ),
   $expr->andX(
      'expr3',
      'expr4'
   ),
);
Run Code Online (Sandbox Code Playgroud)

这将产生一个WHERE

$andXA = $expr>andX();
$andXB = $expr->andX();
$orX = $expr->orX();

$andXA->add('expr1');
$andXA->add('expr2');

$andXB->add('expr3');
$andXB->add('expr4');

$orX->add($andXA);
$orX->add($andXB);
Run Code Online (Sandbox Code Playgroud)

处理参数

为了消除与参数的一些混淆,DQL 不支持值数组。DQL 只是查询 Doctrine 应用程序已知的对象符号的标准化方法。

然而,QueryBuilderORM 和 DBAL 以及Doctrine\DBAL\Connection::executeQuery方法的 Doctrine 2.1+确实支持参数化一组值和重复使用相同命名的参数,与不同PDOMySQLi准备好的语句。[原文] . 在内部,Doctrine 会将参数值数组和重复的参数占位符值转换为单独的参数占位符,以发送到 PDO 准备好的语句。

$qb
   ->orWhere($andXA, $andXB);
Run Code Online (Sandbox Code Playgroud)

生成的 SQL 输出。

WHERE ((expr1 AND expr2) OR (expr3 AND expr4))
Run Code Online (Sandbox Code Playgroud)

结果参数:

array(array("a","b","c"),1,1)
Run Code Online (Sandbox Code Playgroud)

防止通配符%注入

要使用带有参数占位符的 like 语句,您必须指定通配符%_setParameter()值内。

$expr = $em->getExpressionBuilder();
$qb
    ->where($expr->andX(
        $expr->in('cn.a', ':a'),
        $expr->lt('cn.b', ':b'),
        $expr->gt('cn.c', ':b')
    ))
    ->setParameter('a', ['a', 'b', 'c'])
    ->setParameter('b', 1);
Run Code Online (Sandbox Code Playgroud)

不利的一面是,如果变量还包含通配符,则可能会产生不需要的结果。为了防止通配符注入,您可以指定如何转义查询中的通配符,这将与查询构建器和参数占位符一起使用。

WHERE w0_.a IN(?)
AND w0_.b < ?
AND w0_.c > ?
Run Code Online (Sandbox Code Playgroud)

DBAL Expression Builder,从 2.7 - 4.0.x-dev ( current ) 开始,支持将转义字符作为第三个参数。[原文]

array(array("a","b","c"),1,1)
Run Code Online (Sandbox Code Playgroud)

结果 SQL 查询:

$qb->setParameter(0, '%' . $value . '%');
Run Code Online (Sandbox Code Playgroud)

结果参数:

array("%test#%%")
Run Code Online (Sandbox Code Playgroud)

  • @zozo 一般来说,使用反斜杠作为“LIKE”通配符或 SQL 查询的转义字符被认为是不好的做法。由于它不是 ANSI SQL 标准,因此它与 PHP 转义字符相同,并且转义字符可能会根据[服务器配置](https://dev.mysql.com/doc/refman/5.7/en/sql- mode.html#sqlmode_no_backslash_escapes)。请参阅此问题[转义MySQL通配符](/sf/answers/257870791/)以获得更详细的答案。 (2认同)