查看general
MySQL日志,当我将mysqli
与PHP的参数化查询一起使用时,我看到如下语句:
Prepare SELECT * FROM my_table WHERE id = ?
Execute SELECT * FROM my_table WHERE id = 9
Prepare INSERT INTO my_table SET name = ?
Execute INSERT INTO my_table SET name = 'Alex'
这让我感到温暖和模糊,因为我清楚地看到,首先,我的查询是在两个单独的语句中发送的,而它们,我的参数,在两个单独的语句中。
但是当使用ORM(在这种情况下是教义)时,我看到以下内容:
Query SELECT t0.id AS id_1, t0.name AS name_2 FROM my_table t0 WHERE t0.id = '9'
Query START TRANSACTION
Query INSERT INTO my_table (name) VALUES ('Alex')
Query COMMIT
这让我感到警觉,因为我没有看到发送的语句顺序与参数相同。一次性语句+参数。
我对此的问题是:
- Doctrine 是否真的使用参数化语句,为什么它不做 MySQL 所做的 - 记录两个数据包,就像
mysqli
本机所做的那样? - 无论它现在在做什么,Doctrine都能免受注射攻击吗?
- 当 Doctrine 将语句和参数集中到每个查询的同一个查询中时,它如何免受攻击? 它真的在这里做其他事情吗?
Doctrine在大多数情况下在内部使用PDO。
http://php.net/manual/en/pdo.prepare.php 部分说:
PDO 将为本机不支持它们的驱动程序模拟预准备语句/绑定参数,并且还可以将命名或问号样式参数标记重写为更合适的内容(如果驱动程序支持一种样式,但不支持另一种样式)。
启用预准备语句的模拟后,应用运行的prepare()
基本上是无操作的。PDO 保存您的 SQL 字符串,但此时不会将 SQL 查询发送到数据库服务器。
然后,当您调用execute()
时,它会使用适当的字符串转义将您的参数值复制到查询中的适当位置。然后,它最终将最后一个字符串发送到数据库服务器。数据库服务器上不进行任何准备,它只是按原样执行查询。
这也是PDO支持所有品牌RDBMS的位置(?
)和命名(:param
)参数的方式,即使大多数品牌支持一种或另一种样式,但不能同时支持两种样式。
如果模拟预准备语句,则应看到"准备"和"执行"行都显示在查询日志中。我相信这可以在教义中以这种方式完成:
$pdo = $entityManager->getConnection()->getWrappedConnection();
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
虽然我不具体了解 Doctrine 库,但我可以谈谈 MySQL 日志以及就数据库而言正在发生的事情:
第一个示例(准备/执行)使用预准备语句,而后一个示例(查询)不使用。 预准备语句有许多优点,主要是与性能和安全性相关的(如您所提到的,避免SQL注入),我个人会避免使用不使用预准备语句的ORM,因为它们通常被认为是从应用程序内执行查询的最佳实践。
这并不是说 Doctrine 没有在内部做一些清理措施来防止 SQL 注入,我当然希望是这样;同时,使用准备好的语句会更有效,我不知道为什么他们不会。