15

我正在做一个 Doctrine 查询,我必须在 where 子句中进行通配符匹配。我应该如何转义要插入的变量?

我想得到的查询:

SELECT u.* FROM User as u WHERE name LIKE %var%

到目前为止的php代码:

   $query = Doctrine_Query::create()
                ->from('User u')
                ->where();

where 子句中应该包含什么?我要匹配的变量是 $name

4

2 回答 2

36

没有人正确回答你的问题,所以我会尝试一下。

->where('u.name LIKE ?', array("%$name%"));
->where('u.username LIKE ?', '%'.$username.'%')

这些都不安全。让我解释几个场景。

方案 1

想象一下,您想让用户搜索匹配的用户名,但您永远不想列出所有用户名。也许您不希望有人轻易窃取您的一百万个用户名列表。在这段代码之前的某个地方,你做了这样的事情:

if (strlen(trim($name)) < 5) throw Boogey_Monster_Exception();

您认为这会阻止某人将该字段留空并拉下所有用户名的列表......但实际上用户可以提交“_____”或“%%%%%”或任何类似的东西来获取所有用户名的列表,而不仅仅是匹配 5 个或更多已知字符。

我亲眼目睹了在几个大型公共网站上使用这种形式的攻击。

方案 2

您有一个拥有大量用户和大量用户数据的网站。您的用户表中有 10,000,000 行。您希望使站点的用户能够通过搜索已知前缀来找到另一个用户的用户名。

所以你写了一些这样的代码,从上面的例子中稍微修改一下,在搜索字符串之后只有一个通配符。

->where('u.name LIKE ?', array("$name%"));

如果您在 u.name 上有索引,则此 LIKE 查询将使用该索引。因此,如果用户提交 $name="john",那么此查询将有效匹配 johndoe、johnwayne、johnwaynegacy 等用户。

但是,如果用户改为提交 $name="%john",则此查询不再使用索引,现在需要进行全表扫描。在一个非常大的数据库上,这可能是一个非常慢的查询。

SQLi 上的 MySQL 手册提到了同样的事情(第 78-79 页),我在 Google 上搜索了一些查询性能缓慢的示例,并找到了一个链接。

这听起来可能没什么大不了的,但是对于由 RDBMS 支持的站点,RDBMS 通常是一个重要的瓶颈,并且大部分性能工程都围绕着减少对 RDBMS 的争用展开。如果您有少数用户发起攻击,将数据库句柄绑定 60 多秒,并且您有一个小的数据库句柄池,您可以看到它如何快速扩展以垄断您的所有数据库句柄并阻止合法用户从能够得到一个。

链接

http://dev.mysql.com/tech-resources/articles/guide-to-php-security-ch3.pdf

http://forums.mysql.com/read.php?24,13397,13397

解决方案

无论如何,更好的解决方案(如上面链接的 MySQL 手册和评论者 @Maxence 所述,是使用 addcslashes()):

$username = addcslashes("%something_", "%_");

请注意,由于这里的 sql 示例使用准备好的语句,它们完全不受 sql 注入的影响,因此没有必要或不希望使用 mysql_real_escape_string(); 它执行的转义仅仅是为了防止 sql 注入。我们试图阻止的是通配符注入,这需要一个函数来转义两个 sql 通配符“%”和“_”。

于 2011-10-25T17:58:06.177 回答
-3

Doctrine 的文档发生了一些不好的事情,所以这里是Google 副本(查看Like Expressions部分)

...
->where('u.name LIKE ?', array("%$name%"));
于 2010-05-16T08:14:55.583 回答