【问题标题】:How to escape LIKE %$var% with Doctrine?如何使用 Doctrine 逃避 LIKE %$var%?
【发布时间】:2010-05-16 08:08:05
【问题描述】:

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

我想得到的查询:

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

到目前为止的php代码:

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

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

【问题讨论】:

    标签: doctrine doctrine-1.2


    【解决方案1】:

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

    ->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 通配符“%”和“_”。

    【讨论】:

    • 我认为避免对数据库造成高负担的一个很好的解决方案是避免 LIKE 查询并使用像 sphinxs 这样的搜索引擎。太棒了!
    • @mehaase 我看到你给其他答案 -1,但你的方法也不安全,因为你忘记了转义字符本身。所以,至少它应该是这样的:addcslashes('%something_', '\\%_');。请记住,在 MySQL 中,'\\something' LIKE '\\something' 的计算结果为 FALSE,但 '\\something' LIKE '\\\\something' 的计算结果为 TRUE ;-)
    • err... 这个问题没有具体说明您的两种情况。此答案中的信息值得考虑,但您对其他答案的-1 对我来说似乎是错误的......其他答案对于该问题是正确的。
    • @JordanLev 你没抓住重点。问题是如何逃避这个查询。如果您没有转义通配符,则说明您没有正确转义查询。我的回答说明了为什么通配符具有潜在危险以及如何逃避它们。我 -1 错误的其他问题。这是一个常见的错误,但它仍然是一个错误。如果您有任何具体的反馈,请告诉我。我总是喜欢纠正错误信息。
    • @mehaase ,我理解你的意思,我非常感谢你在这里提供的信息。要添加的一个具体细节是您的解决方案不适用于 SQLite 数据库,因为除非您明确告诉它使用反斜杠作为特定 LIKE ... 条件的转义字符,否则不会使用 c 样式转义。 (参见sqlite.org/lang_expr.html 的“字面值”部分)。不幸的是,我不知道如何使用 Doctrine 的查询构建器(我只使用 DBAL)来完成这项工作,但这里解释了这个概念:stackoverflow.com/a/7323498/477513
    【解决方案2】:

    Doctrine 的文档出了点问题,所以这里是 Google copy(检查 Like Expressions 部分)

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

    【讨论】:

    • 如果 $name 中有一个特殊字符,如 '%' 或 '_' 会发生什么?您应该添加 addcslashes($name, '%_')
    • addcslashes();?无论您使用 ORM 还是本机 sql,都无需为查询添加斜线。 Doctrine 负责正确转义,而不会用斜杠乱扔数据。对于原生 mysql,使用mysql_real_escape_string($string);。对于其他人,请检查您的文档;只是不要做addcslashes();
    • @adlawson:学说 不会 转义 %_ 以获取 LIKE 表达式。这必须手动处理。
    • -1 不安全。尽管公平地说,很少有开发人员了解 = 和 LIKE 之间的区别。 (他们认为 LIKE 是 = 的超集,它增加了神奇的模式匹配能力。事实并非如此。)在 LIKE 中不转义通配符可能导致数据泄露或拒绝服务(通过运行异常复杂的模式查询)。
    • 如果名称中有百分号,则通配符匹配会匹配一些额外的东西。这可能不好,但也可能无关紧要(视情况而定)......所以仅仅说这个答案完全错误是不准确的。
    猜你喜欢
    • 2011-06-01
    • 2020-02-14
    • 1970-01-01
    • 2011-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多