Doctrine expr - "literal" 函数是否在内部使用准备好的语句?
Doctrine expr - does "literal" function use prepared statements internally?
对于通常情况,我们有 andWhere/orWhere 与 setParameters 配对的函数可以正确防止注入。
我的情况有点复杂,我想确保一切安全。如果我正确阅读了教义代码,似乎使用文字具有相同的效果,但我不确定......你能不能。 confirm/infirm 以下 2 种情况是安全的(都可以防止 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.
第二种情况
$expr = $queryBuilder->expr()->orX();
$expr->add($queryBuilder->expr()->like('entityName.field', $queryBuilder->expr()->literal(addcslashes($rule->getValue(), '%_') . '%')));
简而言之,您提供的所有陈述都不安全。
Query\Expr
中的方法不会自动将您的值转换为参数占位符。
Query\Expr::literal
的解释
有效地使用 Query\Expr::literal
仅将值转换为 DQL 语句的文字字符串值,并根据需要将提供的值用引号引起来。虽然该方法确实从提供的值中转义了单引号,但这样做并不能防止所有 SQL 注入方法。 [sic]
在你的第一个案例中
$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());
如果$rule->getValue()
是一个实数,得到的SQL语句将变成一个"literal"数值。
WHERE (
(alias.column - 10 < 2)
OR
(...)
)
如果 $rule->getValue()
和 self::MAX_VALUE
是数字字符串(这可能会产生意想不到的结果),结果 SQL 输出将是:
WHERE (
(alias.column - '10' < '2')
OR
(...)
)
并且 $qb->getQuery()->getParameters()
将是一个空的 ArrayCollection
,因为没有添加其他参数。
防止SQL注入
为了确保您的语句免受 SQL 注入,您 必须 在语句中声明参数占位符并使用 setParameter
到 bind the parameter values to the placeholders.
$orX->add(
$expr->lt(
'entityName.field - :rule_value',
':max'
)
);
$qb->setParameter('rule_value', $rule->getValue());
$qb->setParameter('max', self::MAX_VALUE);
如果您有多个占位符,则需要适当地跟踪和绑定它们。
$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);
处理嵌套条件
来自您评论中的问题:
How do you handle a variable number of conditions of andWhere mixed
with orWhere without expr? Something like WHERE condition1 and
(orCondition1 OR orCondition2 or... OR orConditionN)
您可以使用 andX
或 orX
.
的所需分组来创建多个嵌套条件以提供给 WHERE
子句
$expr->orX(
$expr->andX(
'expr1',
'expr2'
),
$expr->andX(
'expr3',
'expr4'
),
);
或
$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);
或者,您可以使用 andWhere
或 orWhere
将表达式添加到主 WHERE
子句部分,但您需要使用表达式生成器来更改嵌套条件分组.
$qb
->orWhere($andXA, $andXB);
这将产生一个 WHERE
子句,如
WHERE ((expr1 AND expr2) OR (expr3 AND expr4))
处理参数
为了消除与参数的混淆,DQL 不支持值数组。 DQL 只是一种查询 Doctrine 应用程序已知的对象符号的标准化方法。
然而,与 PDO
或 MySQLi
准备好的语句。 [sic]。在内部,Doctrine 会将参数值数组和重复的参数占位符值转换为单独的参数占位符,以发送到 PDO 准备语句。
$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);
结果 SQL 输出。
WHERE w0_.a IN(?)
AND w0_.b < ?
AND w0_.c > ?
结果参数:
array(array("a","b","c"),1,1)
防止通配符%
注入
要使用带有参数占位符的 like 语句,您必须在 setParameter()
值中指定通配符 %
或 _
。
$qb->setParameter(0, '%' . $value . '%');
不利的一面是,如果变量还包含通配符,则可能会产生不良结果。为防止通配符注入,您可以指定如何转义查询中的通配符符号,这将与查询构建器和参数占位符一起使用。
$qb
->where($expr->like('a', ':a ESCAPE ' . $expr->literal('#')))
->setParameter('a', '%' . preg_replace('/([#%_])/', '#[=23=]', $value) . '%');
DBAL 表达式构建器,从 2.7 - 4.0.x-dev (当前),支持转义字符作为第三个参数。 [sic]
$value = 'test%';
$d_qb = $em->getConnection()->createQueryBuilder();
$d_expr = $d_qb->expr();
$d_qb
->where($d_expr->like('a', ':a', '#'))
->setParameter('a', '%' . preg_replace('/([#%_])/', '#[=24=]', $value) . '%');
结果 SQL 查询:
WHERE c0_.column LIKE ? ESCAPE '#'
结果参数:
array("%test#%%")
对于通常情况,我们有 andWhere/orWhere 与 setParameters 配对的函数可以正确防止注入。
我的情况有点复杂,我想确保一切安全。如果我正确阅读了教义代码,似乎使用文字具有相同的效果,但我不确定......你能不能。 confirm/infirm 以下 2 种情况是安全的(都可以防止 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.
第二种情况
$expr = $queryBuilder->expr()->orX();
$expr->add($queryBuilder->expr()->like('entityName.field', $queryBuilder->expr()->literal(addcslashes($rule->getValue(), '%_') . '%')));
简而言之,您提供的所有陈述都不安全。
Query\Expr
中的方法不会自动将您的值转换为参数占位符。
Query\Expr::literal
的解释
有效地使用 Query\Expr::literal
仅将值转换为 DQL 语句的文字字符串值,并根据需要将提供的值用引号引起来。虽然该方法确实从提供的值中转义了单引号,但这样做并不能防止所有 SQL 注入方法。 [sic]
在你的第一个案例中
$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());
如果$rule->getValue()
是一个实数,得到的SQL语句将变成一个"literal"数值。
WHERE (
(alias.column - 10 < 2)
OR
(...)
)
如果 $rule->getValue()
和 self::MAX_VALUE
是数字字符串(这可能会产生意想不到的结果),结果 SQL 输出将是:
WHERE (
(alias.column - '10' < '2')
OR
(...)
)
并且 $qb->getQuery()->getParameters()
将是一个空的 ArrayCollection
,因为没有添加其他参数。
防止SQL注入
为了确保您的语句免受 SQL 注入,您 必须 在语句中声明参数占位符并使用 setParameter
到 bind the parameter values to the placeholders.
$orX->add(
$expr->lt(
'entityName.field - :rule_value',
':max'
)
);
$qb->setParameter('rule_value', $rule->getValue());
$qb->setParameter('max', self::MAX_VALUE);
如果您有多个占位符,则需要适当地跟踪和绑定它们。
$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);
处理嵌套条件
来自您评论中的问题:
How do you handle a variable number of conditions of andWhere mixed with orWhere without expr? Something like WHERE condition1 and (orCondition1 OR orCondition2 or... OR orConditionN)
您可以使用 andX
或 orX
.
WHERE
子句
$expr->orX(
$expr->andX(
'expr1',
'expr2'
),
$expr->andX(
'expr3',
'expr4'
),
);
或
$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);
或者,您可以使用 andWhere
或 orWhere
将表达式添加到主 WHERE
子句部分,但您需要使用表达式生成器来更改嵌套条件分组.
$qb
->orWhere($andXA, $andXB);
这将产生一个 WHERE
子句,如
WHERE ((expr1 AND expr2) OR (expr3 AND expr4))
处理参数
为了消除与参数的混淆,DQL 不支持值数组。 DQL 只是一种查询 Doctrine 应用程序已知的对象符号的标准化方法。
然而,与 PDO
或 MySQLi
准备好的语句。 [sic]。在内部,Doctrine 会将参数值数组和重复的参数占位符值转换为单独的参数占位符,以发送到 PDO 准备语句。
$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);
结果 SQL 输出。
WHERE w0_.a IN(?)
AND w0_.b < ?
AND w0_.c > ?
结果参数:
array(array("a","b","c"),1,1)
防止通配符%
注入
要使用带有参数占位符的 like 语句,您必须在 setParameter()
值中指定通配符 %
或 _
。
$qb->setParameter(0, '%' . $value . '%');
不利的一面是,如果变量还包含通配符,则可能会产生不良结果。为防止通配符注入,您可以指定如何转义查询中的通配符符号,这将与查询构建器和参数占位符一起使用。
$qb
->where($expr->like('a', ':a ESCAPE ' . $expr->literal('#')))
->setParameter('a', '%' . preg_replace('/([#%_])/', '#[=23=]', $value) . '%');
DBAL 表达式构建器,从 2.7 - 4.0.x-dev (当前),支持转义字符作为第三个参数。 [sic]
$value = 'test%';
$d_qb = $em->getConnection()->createQueryBuilder();
$d_expr = $d_qb->expr();
$d_qb
->where($d_expr->like('a', ':a', '#'))
->setParameter('a', '%' . preg_replace('/([#%_])/', '#[=24=]', $value) . '%');
结果 SQL 查询:
WHERE c0_.column LIKE ? ESCAPE '#'
结果参数:
array("%test#%%")