转义 MySQL 通配符
Posted
技术标签:
【中文标题】转义 MySQL 通配符【英文标题】:Escaping MySQL wild cards 【发布时间】:2010-09-10 10:04:36 【问题描述】:在我正在使用的旧服务器上,我无法使用准备好的语句,我目前正试图在将用户输入发送到 mysql 之前完全转义它。
为此,我使用了 php 函数 mysql_real_escape_string
。
由于此函数不会 % 和 _,因此我也使用 addcslashes
转义这些。
当我发送类似:
test_test " '
到数据库再读回数据库显示:
test\_test " '
看着这个,我不明白为什么 _ 前面有反斜杠,但 " 和 ' 没有。 因为它们都用 \ 肯定 _ ' 进行了转义,并且 " 应该看起来都一样,即所有转义字符都可见或都不可见。
转义的 \s 是否会自动屏蔽掉
谁能解释一下?
【问题讨论】:
【参考方案1】:_
和 %
通常不是 MySQL 中的通配符,不应为了将它们放入普通字符串文字而对其进行转义。 mysql_real_escape_string
是正确且足够的。不应使用addcslashes
。
_
和 %
仅在 LIKE
-matching 的上下文中是特殊的。当您想在 LIKE
语句中准备字符串以供文字使用时,以便 100%
匹配 100% 而不仅仅是任何以 100 开头的字符串,您需要担心两个级别的转义。
第一个是 LIKE 转义。 LIKE 处理完全在 SQL 内部进行,如果您想将文字字符串转换为文字 LIKE 表达式,您必须执行此步骤即使您使用的是参数化查询!
在这个方案中,_
和 %
是特殊的,必须转义。转义字符也必须转义。根据 ANSI SQL,除了这些 不能 之外的字符被转义:\'
是错误的。 (虽然 MySQL 通常会让你侥幸逃脱。)
完成此操作后,您将进入第二级转义,即普通的旧字符串文字转义。这发生在 SQL 之外,创建 SQL,因此必须在 LIKE 转义步骤之后完成。对于 MySQL,这是 mysql_real_escape_string
和以前一样;对于其他数据库,会有不同的功能,您可以使用参数化查询来避免这样做。
这里导致混淆的问题是,在 MySQL 中,两个嵌套转义步骤都使用反斜杠作为转义字符!因此,如果您想将字符串与文字百分号匹配,则必须使用双反斜杠转义并说LIKE 'something\\%'
。或者,如果这是在 PHP "
文字中,它也使用反斜杠转义,"LIKE 'something\\\\%'"
。啊!
根据 ANSI SQL,这是不正确的,它说:在字符串文字中,反斜杠表示文字反斜杠,转义单引号的方法是 ''
;在 LIKE 表达式中,默认情况下根本没有转义字符。
因此,如果您想以可移植的方式 LIKE-escape,您应该使用 LIKE ... ESCAPE ...
构造覆盖默认(错误)行为并指定您自己的转义字符。为了理智,我们将选择该死的反斜杠以外的其他东西!
function like($s, $e)
return str_replace(array($e, '_', '%'), array($e.$e, $e.'_', $e.'%'), $s);
$escapedname= mysql_real_escape_string(like($name, '='));
$query= "... WHERE name LIKE '%$escapedname%' ESCAPE '=' AND ...";
或带参数(例如在 PDO 中):
$q= $db->prepare("... WHERE name LIKE ? ESCAPE '=' AND ...");
$q->bindValue(1, '%'.like($name, '=').'%', PDO::PARAM_STR);
(如果您想要更多的可移植性聚会时间,您还可以尝试考虑 MS SQL Server 和 Sybase,其中[
字符在LIKE
语句中也错误地是特殊的并且必须被转义. 啊。)
【讨论】:
我会再次为“该死的反斜杠”+1。 谢谢,现在就吸收这个……这真的帮助我扩展了我的基础知识。愚蠢的是,即使我实际上并没有使用任何 LIKE 语句,我也在转义 % 和 _ 并且因为我认为(请确认)% 和 _ 仅在 LIKE 语句的上下文中是狂野的,我实际上是在浪费我的时间。但这让我想,当它在 LIKE 语句的上下文中时,你为什么要转义 % 或 _ 。当然,使用 LIKE 语句的唯一原因是您可以使用它的通配符。 (请原谅我对这方面的知识有限) 当然,但是希望能够搜索文字%
或 _
字符是非常自然的。如果用户在前端搜索50%
,他们可能意味着他们正在寻找包含50%
的字符串,而不仅仅是其中包含50
的任何字符串。
我无法编辑答案,但有一个小错误:在 str_replace() 行中:$e_ 是一个不存在的变量。相反,请使用“$e_”
在使用例如 utf8mb4 语言特定排序规则时还要注意bugs.mysql.com/bug.php?id=39808(请参阅此处的列表hastebin.com/acoqedajij)。【参考方案2】:
惊讶了这么多年没有人提起它,但如果你不需要做复杂的通配符匹配(例如foo%baz
),我认为INSTR
/LOCATE
/POSITION
,@987654325 @、RIGHT
等就足够了。在我的所有情况下,我只使用LIKE
匹配字符串中的任何位置(例如%foobar%
),所以在所有关于转义LIKE
模式的恐怖故事之后,我现在使用INSTR
而是。
等价于value LIKE '%foobar%'
(匹配任何地方):
INSTR(value, 'foobar') > 0
等价于value LIKE 'foobar%'
(开始时匹配):
INSTR(value, 'foobar') = 1
等价于value LIKE '%foobar'
(最后匹配):
RIGHT(value, 6) = 'foobar'
它可能不那么直接和容易记住,最后匹配的解决方案可能会以某种方式进行改进以使其更加通用。但是这些替代方案至少应该让您在安全方面放心,因为它绕过了任何自滚转义的需要,并且不需要您更改实际参数值(无论如何使用准备好的语句时)。
【讨论】:
以上是关于转义 MySQL 通配符的主要内容,如果未能解决你的问题,请参考以下文章